func (c *Client) EnsureBuildableJob(spec *prowapi.ProwJobSpec) error {
	var jobInfo *JobInfo

	// wait at most 20 seconds for the job to appear
	getJobInfoBackoff := wait.Backoff{
		Duration: time.Duration(10) * time.Second,
		Factor:   1,
		Jitter:   0,
		Steps:    2,
	}

	getJobErr := wait.ExponentialBackoff(getJobInfoBackoff, func() (bool, error) {
		var jobErr error
		jobInfo, jobErr = c.GetJobInfo(spec)

		if jobErr != nil && !strings.Contains(strings.ToLower(jobErr.Error()), "404 not found") {
			return false, jobErr
		}

		return jobInfo != nil, nil
	})

	if getJobErr != nil {
		return fmt.Errorf("Job %v does not exist", spec.Job)
	}

	isParameterized := c.JobParameterized(jobInfo)

	c.logger.Tracef("JobHasParameters: %v", isParameterized)

	if isParameterized || len(jobInfo.Builds) > 0 {
		return nil
	}

	buildErr := c.LaunchBuild(spec, nil)

	if buildErr != nil {
		return buildErr
	}

	backoff := wait.Backoff{
		Duration: time.Duration(5) * time.Second,
		Factor:   1,
		Jitter:   1,
		Steps:    10,
	}

	return wait.ExponentialBackoff(backoff, func() (bool, error) {
		c.logger.Debugf("Waiting for job %v to become parameterized", spec.Job)

		jobInfo, _ := c.GetJobInfo(spec)
		isParameterized := false

		if jobInfo != nil {
			isParameterized = c.JobParameterized(jobInfo)

			if isParameterized && jobInfo.LastBuild != nil {
				c.logger.Debugf("Job %v is now parameterized, aborting the build", spec.Job)
				err := c.Abort(getJobName(spec), jobInfo.LastBuild)

				if err != nil {
					c.logger.Infof("Couldn't abort build #%v for job %v: %v", jobInfo.LastBuild.Number, spec.Job, err)
				}
			}
		}

		// don't stop on (possibly) intermittent errors
		return isParameterized, nil
	})
}

// EnsureBuildableJob attempts to detect a job that hasn't yet ran and populated
// its parameters. If detected, it tries to run a build until the job parameters
// are processed, then it aborts the build.

EnsureBuildableJob attempts to detect a job that hasn t yet ran and populated its parameters . If detected it tries to run a build until the job parameters are processed then it aborts the build .


func (c *Client) LaunchBuild(spec *prowapi.ProwJobSpec, params url.Values) error {
	var path string

	if params != nil {
		path = getBuildWithParametersPath(spec)
	} else {
		path = getBuildPath(spec)
	}

	c.logger.Debugf("getBuildPath/getBuildWithParametersPath: %s", path)

	resp, err := c.request(http.MethodPost, path, params, true)

	if err != nil {
		return err
	}

	defer resp.Body.Close()

	if resp.StatusCode != 201 {
		return fmt.Errorf("response not 201: %s", resp.Status)
	}

	return nil
}

func ( c * Client ) LaunchBuild ( spec * prowapi . ProwJobSpec , params url . Values ) error { var path string if params != nil { path = getBuildWithParametersPath ( spec ) } else { path = getBuildPath ( spec ) } c . logger . Debugf ( " " , path ) resp , err := c . request ( http . MethodPost , path , params , true ) if err != nil { return err } defer resp . Body . Close ( ) if resp . StatusCode != 201 { return fmt . Errorf ( " " , resp . Status ) } return nil }

// LaunchBuild launches a regular or parameterized Jenkins build, depending on
// whether or not we have `params` to POST

LaunchBuild launches a regular or parameterized Jenkins build depending on whether or not we have params to POST


func (c *Client) Build(pj *prowapi.ProwJob, buildID string) error {
	c.logger.WithFields(pjutil.ProwJobFields(pj)).Info("Build")
	return c.BuildFromSpec(&pj.Spec, buildID, pj.ObjectMeta.Name)
}

func ( c * Client ) Build ( pj * prowapi . ProwJob , buildID string ) error { c . logger . WithFields ( pjutil . ProwJobFields ( pj ) ) . Info ( " " ) return c . BuildFromSpec ( & pj . Spec , buildID , pj . ObjectMeta . Name ) }

// Build triggers a Jenkins build for the provided ProwJob. The name of
// the ProwJob is going to be used as the Prow Job ID parameter that will
// help us track the build before it's scheduled by Jenkins.

Build triggers a Jenkins build for the provided ProwJob . The name of the ProwJob is going to be used as the Prow Job ID parameter that will help us track the build before it s scheduled by Jenkins .


func (c *Client) BuildFromSpec(spec *prowapi.ProwJobSpec, buildID, prowJobID string) error {
	if c.dryRun {
		return nil
	}
	env, err := downwardapi.EnvForSpec(downwardapi.NewJobSpec(*spec, buildID, prowJobID))
	if err != nil {
		return err
	}
	params := url.Values{}
	for key, value := range env {
		params.Set(key, value)
	}

	if err := c.EnsureBuildableJob(spec); err != nil {
		return fmt.Errorf("Job %v cannot be build: %v", spec.Job, err)
	}

	return c.LaunchBuild(spec, params)
}

func ( c * Client ) BuildFromSpec ( spec * prowapi . ProwJobSpec , buildID , prowJobID string ) error { if c . dryRun { return nil } env , err := downwardapi . EnvForSpec ( downwardapi . NewJobSpec ( * spec , buildID , prowJobID ) ) if err != nil { return err } params := url . Values { } for key , value := range env { params . Set ( key , value ) } if err := c . EnsureBuildableJob ( spec ) ; err != nil { return fmt . Errorf ( " " , spec . Job , err ) } return c . LaunchBuild ( spec , params ) }

// BuildFromSpec triggers a Jenkins build for the provided ProwJobSpec.
// prowJobID helps us track the build before it's scheduled by Jenkins.

BuildFromSpec triggers a Jenkins build for the provided ProwJobSpec . prowJobID helps us track the build before it s scheduled by Jenkins .


func (c *Client) GetEnqueuedBuilds(jobs []BuildQueryParams) (map[string]Build, error) {
	c.logger.Debug("GetEnqueuedBuilds")

	data, err := c.Get("/queue/api/json?tree=items[task[name],actions[parameters[name,value]]]")
	if err != nil {
		return nil, fmt.Errorf("cannot list builds from the queue: %v", err)
	}
	page := struct {
		QueuedBuilds []Build `json:"items"`
	}{}
	if err := json.Unmarshal(data, &page); err != nil {
		return nil, fmt.Errorf("cannot unmarshal builds from the queue: %v", err)
	}
	jenkinsBuilds := make(map[string]Build)
	for _, jb := range page.QueuedBuilds {
		prowJobID := jb.ProwJobID()
		// Ignore builds with missing buildID parameters.
		if prowJobID == "" {
			continue
		}
		// Ignore builds for jobs we didn't ask for.
		var exists bool
		for _, job := range jobs {
			if prowJobID == job.ProwJobID {
				exists = true
				break
			}
		}
		if !exists {
			continue
		}
		jb.enqueued = true
		jenkinsBuilds[prowJobID] = jb
	}
	return jenkinsBuilds, nil
}

// GetEnqueuedBuilds lists all enqueued builds for the provided jobs.

GetEnqueuedBuilds lists all enqueued builds for the provided jobs .


func (c *Client) Abort(job string, build *Build) error {
	c.logger.Debugf("Abort(%v %v)", job, build.Number)
	if c.dryRun {
		return nil
	}
	resp, err := c.request(http.MethodPost, fmt.Sprintf("/job/%s/%d/stop", job, build.Number), nil, false)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return fmt.Errorf("response not 2XX: %s", resp.Status)
	}
	return nil
}

func ( c * Client ) Abort ( job string , build * Build ) error { c . logger . Debugf ( " " , job , build . Number ) if c . dryRun { return nil } resp , err := c . request ( http . MethodPost , fmt . Sprintf ( " " , job , build . Number ) , nil , false ) if err != nil { return err } defer resp . Body . Close ( ) if resp . StatusCode < 200 || resp . StatusCode >= 300 { return fmt . Errorf ( " " , resp . Status ) } return nil }

// Abort aborts the provided Jenkins build for job.

Abort aborts the provided Jenkins build for job .


func PresubmitToJobSpec(pre config.Presubmit) *downwardapi.JobSpec {
	return &downwardapi.JobSpec{
		Type: prowapi.PresubmitJob,
		Job:  pre.Name,
	}
}

func PresubmitToJobSpec ( pre config . Presubmit ) * downwardapi . JobSpec { return & downwardapi . JobSpec { Type : prowapi . PresubmitJob , Job : pre . Name , } }

// PresubmitToJobSpec generates a downwardapi.JobSpec out of a Presubmit.
// Useful for figuring out GCS paths when parsing jobs out
// of a prow config.

PresubmitToJobSpec generates a downwardapi . JobSpec out of a Presubmit . Useful for figuring out GCS paths when parsing jobs out of a prow config .


func PostsubmitToJobSpec(post config.Postsubmit) *downwardapi.JobSpec {
	return &downwardapi.JobSpec{
		Type: prowapi.PostsubmitJob,
		Job:  post.Name,
	}
}

func PostsubmitToJobSpec ( post config . Postsubmit ) * downwardapi . JobSpec { return & downwardapi . JobSpec { Type : prowapi . PostsubmitJob , Job : post . Name , } }

// PostsubmitToJobSpec generates a downwardapi.JobSpec out of a Postsubmit.
// Useful for figuring out GCS paths when parsing jobs out
// of a prow config.

PostsubmitToJobSpec generates a downwardapi . JobSpec out of a Postsubmit . Useful for figuring out GCS paths when parsing jobs out of a prow config .


func PeriodicToJobSpec(periodic config.Periodic) *downwardapi.JobSpec {
	return &downwardapi.JobSpec{
		Type: prowapi.PeriodicJob,
		Job:  periodic.Name,
	}
}

func PeriodicToJobSpec ( periodic config . Periodic ) * downwardapi . JobSpec { return & downwardapi . JobSpec { Type : prowapi . PeriodicJob , Job : periodic . Name , } }

// PeriodicToJobSpec generates a downwardapi.JobSpec out of a Periodic.
// Useful for figuring out GCS paths when parsing jobs out
// of a prow config.

PeriodicToJobSpec generates a downwardapi . JobSpec out of a Periodic . Useful for figuring out GCS paths when parsing jobs out of a prow config .


func GetBuildID(name, totURL string) (string, error) {
	if totURL == "" {
		return node.Generate().String(), nil
	}
	var err error
	url, err := url.Parse(totURL)
	if err != nil {
		return "", fmt.Errorf("invalid tot url: %v", err)
	}
	url.Path = path.Join(url.Path, "vend", name)
	sleepDuration := 100 * time.Millisecond
	for retries := 0; retries < 10; retries++ {
		if retries > 0 {
			sleep(sleepDuration)
			sleepDuration = sleepDuration * 2
		}
		var resp *http.Response
		resp, err = http.Get(url.String())
		if err != nil {
			continue
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			err = fmt.Errorf("got unexpected response from tot: %v", resp.Status)
			continue
		}
		var buf []byte
		buf, err = ioutil.ReadAll(resp.Body)
		if err == nil {
			return string(buf), nil
		}
		return "", err
	}
	return "", err
}

func GetBuildID ( name , totURL string ) ( string , error ) { if totURL == " " { return node . Generate ( ) . String ( ) , nil } var err error url , err := url . Parse ( totURL ) if err != nil { return " " , fmt . Errorf ( " " , err ) } url . Path = path . Join ( url . Path , " " , name ) sleepDuration := 100 * time . Millisecond for retries := 0 ; retries < 10 ; retries ++ { if retries > 0 { sleep ( sleepDuration ) sleepDuration = sleepDuration * 2 } var resp * http . Response resp , err = http . Get ( url . String ( ) ) if err != nil { continue } defer resp . Body . Close ( ) if resp . StatusCode != 200 { err = fmt . Errorf ( " " , resp . Status ) continue } var buf [ ] byte buf , err = ioutil . ReadAll ( resp . Body ) if err == nil { return string ( buf ) , nil } return " " , err } return " " , err }

// GetBuildID calls out to `tot` in order
// to vend build identifier for the job

GetBuildID calls out to tot in order to vend build identifier for the job


func listGcsObjects(ctx context.Context, client *storage.Client, bucketName, prefix, delim string) (
	[]string, error) {

	var objects []string
	it := client.Bucket(bucketName).Objects(ctx, &storage.Query{
		Prefix:    prefix,
		Delimiter: delim,
	})

	for {
		attrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return objects, fmt.Errorf("error iterating: %v", err)
		}

		if attrs.Prefix != "" {
			objects = append(objects, path.Base(attrs.Prefix))
		}
	}
	logrus.Info("end of listGcsObjects(...)")
	return objects, nil
}

func listGcsObjects ( ctx context . Context , client * storage . Client , bucketName , prefix , delim string ) ( [ ] string , error ) { var objects [ ] string it := client . Bucket ( bucketName ) . Objects ( ctx , & storage . Query { Prefix : prefix , Delimiter : delim , } ) for { attrs , err := it . Next ( ) if err == iterator . Done { break } if err != nil { return objects , fmt . Errorf ( " " , err ) } if attrs . Prefix != " " { objects = append ( objects , path . Base ( attrs . Prefix ) ) } } logrus . Info ( " " ) return objects , nil }

//listGcsObjects get the slice of gcs objects under a given path


func FindBaseProfile(ctx context.Context, client *storage.Client, bucket, prowJobName, artifactsDirName,
	covProfileName string) ([]byte, error) {

	dirOfJob := path.Join("logs", prowJobName)

	strBuilds, err := listGcsObjects(ctx, client, bucket, dirOfJob+"/", "/")
	if err != nil {
		return nil, fmt.Errorf("error listing gcs objects: %v", err)
	}

	builds := sortBuilds(strBuilds)
	profilePath := ""
	for _, build := range builds {
		buildDirPath := path.Join(dirOfJob, strconv.Itoa(build))
		dirOfStatusJSON := path.Join(buildDirPath, statusJSON)

		statusText, err := readGcsObject(ctx, client, bucket, dirOfStatusJSON)
		if err != nil {
			logrus.Infof("Cannot read finished.json (%s) in bucket '%s'", dirOfStatusJSON, bucket)
		} else if isBuildSucceeded(statusText) {
			artifactsDirPath := path.Join(buildDirPath, artifactsDirName)
			profilePath = path.Join(artifactsDirPath, covProfileName)
			break
		}
	}
	if profilePath == "" {
		return nil, fmt.Errorf("no healthy build found for job '%s' in bucket '%s'; total # builds = %v", dirOfJob, bucket, len(builds))
	}
	return readGcsObject(ctx, client, bucket, profilePath)
}

func FindBaseProfile ( ctx context . Context , client * storage . Client , bucket , prowJobName , artifactsDirName , covProfileName string ) ( [ ] byte , error ) { dirOfJob := path . Join ( " " , prowJobName ) strBuilds , err := listGcsObjects ( ctx , client , bucket , dirOfJob + " " , " " ) if err != nil { return nil , fmt . Errorf ( " " , err ) } builds := sortBuilds ( strBuilds ) profilePath := " " for _ , build := range builds { buildDirPath := path . Join ( dirOfJob , strconv . Itoa ( build ) ) dirOfStatusJSON := path . Join ( buildDirPath , statusJSON ) statusText , err := readGcsObject ( ctx , client , bucket , dirOfStatusJSON ) if err != nil { logrus . Infof ( " " , dirOfStatusJSON , bucket ) } else if isBuildSucceeded ( statusText ) { artifactsDirPath := path . Join ( buildDirPath , artifactsDirName ) profilePath = path . Join ( artifactsDirPath , covProfileName ) break } } if profilePath == " " { return nil , fmt . Errorf ( " " , dirOfJob , bucket , len ( builds ) ) } return readGcsObject ( ctx , client , bucket , profilePath ) }

// FindBaseProfile finds the coverage profile file from the latest healthy build
// stored in given gcs directory

FindBaseProfile finds the coverage profile file from the latest healthy build stored in given gcs directory


func sortBuilds(strBuilds []string) []int {
	var res []int
	for _, buildStr := range strBuilds {
		num, err := strconv.Atoi(buildStr)
		if err != nil {
			logrus.Infof("Non-int build number found: '%s'", buildStr)
		} else {
			res = append(res, num)
		}
	}
	sort.Sort(sort.Reverse(sort.IntSlice(res)))
	return res
}

func sortBuilds ( strBuilds [ ] string ) [ ] int { var res [ ] int for _ , buildStr := range strBuilds { num , err := strconv . Atoi ( buildStr ) if err != nil { logrus . Infof ( " " , buildStr ) } else { res = append ( res , num ) } } sort . Sort ( sort . Reverse ( sort . IntSlice ( res ) ) ) return res }

// sortBuilds converts all build from str to int and sorts all builds in descending order and
// returns the sorted slice

sortBuilds converts all build from str to int and sorts all builds in descending order and returns the sorted slice


func GetAll(sess *session.Session) ([]string, error) {
	var regions []string
	svc := ec2.New(sess, &aws.Config{Region: aws.String(Default)})
	resp, err := svc.DescribeRegions(nil)
	if err != nil {
		return nil, err
	}
	for _, region := range resp.Regions {
		regions = append(regions, *region.RegionName)
	}
	return regions, nil
}

func GetAll ( sess * session . Session ) ( [ ] string , error ) { var regions [ ] string svc := ec2 . New ( sess , & aws . Config { Region : aws . String ( Default ) } ) resp , err := svc . DescribeRegions ( nil ) if err != nil { return nil , err } for _ , region := range resp . Regions { regions = append ( regions , * region . RegionName ) } return regions , nil }

// GetAll retrieves all regions from the AWS API


func NewEventClient(ghc githubClient, log *logrus.Entry, org, repo string, number int) *EventClient {
	return &EventClient{
		org:    org,
		repo:   repo,
		number: number,

		ghc: ghc,
		log: log,
	}
}

func NewEventClient ( ghc githubClient , log * logrus . Entry , org , repo string , number int ) * EventClient { return & EventClient { org : org , repo : repo , number : number , ghc : ghc , log : log , } }

// NewEventClient creates an EventClient struct. This should be used once per webhook event.

NewEventClient creates an EventClient struct . This should be used once per webhook event .


func (c *EventClient) PruneComments(shouldPrune func(github.IssueComment) bool) {
	c.once.Do(func() {
		botName, err := c.ghc.BotName()
		if err != nil {
			c.log.WithError(err).Error("failed to get the bot's name. Pruning will consider all comments.")
		}
		comments, err := c.ghc.ListIssueComments(c.org, c.repo, c.number)
		if err != nil {
			c.log.WithError(err).Errorf("failed to list comments for %s/%s#%d", c.org, c.repo, c.number)
		}
		if botName != "" {
			for _, comment := range comments {
				if comment.User.Login == botName {
					c.comments = append(c.comments, comment)
				}
			}
		}
	})

	c.lock.Lock()
	defer c.lock.Unlock()

	var remaining []github.IssueComment
	for _, comment := range c.comments {
		removed := false
		if shouldPrune(comment) {
			if err := c.ghc.DeleteComment(c.org, c.repo, comment.ID); err != nil {
				c.log.WithError(err).Errorf("failed to delete stale comment with ID '%d'", comment.ID)
			} else {
				removed = true
			}
		}
		if !removed {
			remaining = append(remaining, comment)
		}
	}
	c.comments = remaining
}

func ( c * EventClient ) PruneComments ( shouldPrune func ( github . IssueComment ) bool ) { c . once . Do ( func ( ) { botName , err := c . ghc . BotName ( ) if err != nil { c . log . WithError ( err ) . Error ( " " ) } comments , err := c . ghc . ListIssueComments ( c . org , c . repo , c . number ) if err != nil { c . log . WithError ( err ) . Errorf ( " " , c . org , c . repo , c . number ) } if botName != " " { for _ , comment := range comments { if comment . User . Login == botName { c . comments = append ( c . comments , comment ) } } } } ) c . lock . Lock ( ) defer c . lock . Unlock ( ) var remaining [ ] github . IssueComment for _ , comment := range c . comments { removed := false if shouldPrune ( comment ) { if err := c . ghc . DeleteComment ( c . org , c . repo , comment . ID ) ; err != nil { c . log . WithError ( err ) . Errorf ( " " , comment . ID ) } else { removed = true } } if ! removed { remaining = append ( remaining , comment ) } } c . comments = remaining }

// PruneComments fetches issue comments if they have not yet been fetched for this webhook event
// and then deletes any bot comments indicated by the func 'shouldPrune'.

PruneComments fetches issue comments if they have not yet been fetched for this webhook event and then deletes any bot comments indicated by the func shouldPrune .


func FormatResponse(to, message, reason string) string {
	format := `@%s: %s

<details>

%s

%s
</details>`

	return fmt.Sprintf(format, to, message, reason, AboutThisBotWithoutCommands)
}

// FormatResponse nicely formats a response to a generic reason.

FormatResponse nicely formats a response to a generic reason .


func FormatSimpleResponse(to, message string) string {
	format := `@%s: %s

<details>

%s
</details>`

	return fmt.Sprintf(format, to, message, AboutThisBotWithoutCommands)
}

// FormatSimpleResponse formats a response that does not warrant additional explanation in the
// details section.

FormatSimpleResponse formats a response that does not warrant additional explanation in the details section .


func FormatICResponse(ic github.IssueComment, s string) string {
	return FormatResponseRaw(ic.Body, ic.HTMLURL, ic.User.Login, s)
}

func FormatICResponse ( ic github . IssueComment , s string ) string { return FormatResponseRaw ( ic . Body , ic . HTMLURL , ic . User . Login , s ) }

// FormatICResponse nicely formats a response to an issue comment.

FormatICResponse nicely formats a response to an issue comment .


func FormatResponseRaw(body, bodyURL, login, reply string) string {
	format := `In response to [this](%s):

%s
`
	// Quote the user's comment by prepending ">" to each line.
	var quoted []string
	for _, l := range strings.Split(body, "\n") {
		quoted = append(quoted, ">"+l)
	}
	return FormatResponse(login, reply, fmt.Sprintf(format, bodyURL, strings.Join(quoted, "\n")))
}

// FormatResponseRaw nicely formats a response for one does not have an issue comment


func (o *Options) Validate() error {
	if o.gcsPath.String() != "" {
		o.Bucket = o.gcsPath.Bucket()
		o.PathPrefix = o.gcsPath.Object()
	}

	if !o.DryRun {
		if o.Bucket == "" {
			return errors.New("GCS upload was requested no GCS bucket was provided")
		}

		if o.GcsCredentialsFile == "" {
			return errors.New("GCS upload was requested but no GCS credentials file was provided")
		}
	}

	return o.GCSConfiguration.Validate()
}

func ( o * Options ) Validate ( ) error { if o . gcsPath . String ( ) != " " { o . Bucket = o . gcsPath . Bucket ( ) o . PathPrefix = o . gcsPath . Object ( ) } if ! o . DryRun { if o . Bucket == " " { return errors . New ( " " ) } if o . GcsCredentialsFile == " " { return errors . New ( " " ) } } return o . GCSConfiguration . Validate ( ) }

// Validate ensures that the set of options are
// self-consistent and valid.

Validate ensures that the set of options are self - consistent and valid .


func Encode(options Options) (string, error) {
	encoded, err := json.Marshal(options)
	return string(encoded), err
}

func Encode ( options Options ) ( string , error ) { encoded , err := json . Marshal ( options ) return string ( encoded ) , err }

// Encode will encode the set of options in the format that
// is expected for the configuration environment variable.

Encode will encode the set of options in the format that is expected for the configuration environment variable .


func RegisterIssueHandler(name string, fn IssueHandler, help HelpProvider) {
	pluginHelp[name] = help
	issueHandlers[name] = fn
}

func RegisterIssueHandler ( name string , fn IssueHandler , help HelpProvider ) { pluginHelp [ name ] = help issueHandlers [ name ] = fn }

// RegisterIssueHandler registers a plugin's github.IssueEvent handler.

RegisterIssueHandler registers a plugin s github . IssueEvent handler .


func RegisterIssueCommentHandler(name string, fn IssueCommentHandler, help HelpProvider) {
	pluginHelp[name] = help
	issueCommentHandlers[name] = fn
}

func RegisterIssueCommentHandler ( name string , fn IssueCommentHandler , help HelpProvider ) { pluginHelp [ name ] = help issueCommentHandlers [ name ] = fn }

// RegisterIssueCommentHandler registers a plugin's github.IssueCommentEvent handler.

RegisterIssueCommentHandler registers a plugin s github . IssueCommentEvent handler .


func RegisterPullRequestHandler(name string, fn PullRequestHandler, help HelpProvider) {
	pluginHelp[name] = help
	pullRequestHandlers[name] = fn
}

func RegisterPullRequestHandler ( name string , fn PullRequestHandler , help HelpProvider ) { pluginHelp [ name ] = help pullRequestHandlers [ name ] = fn }

// RegisterPullRequestHandler registers a plugin's github.PullRequestEvent handler.

RegisterPullRequestHandler registers a plugin s github . PullRequestEvent handler .


func RegisterStatusEventHandler(name string, fn StatusEventHandler, help HelpProvider) {
	pluginHelp[name] = help
	statusEventHandlers[name] = fn
}

func RegisterStatusEventHandler ( name string , fn StatusEventHandler , help HelpProvider ) { pluginHelp [ name ] = help statusEventHandlers [ name ] = fn }

// RegisterStatusEventHandler registers a plugin's github.StatusEvent handler.

RegisterStatusEventHandler registers a plugin s github . StatusEvent handler .


func RegisterPushEventHandler(name string, fn PushEventHandler, help HelpProvider) {
	pluginHelp[name] = help
	pushEventHandlers[name] = fn
}

func RegisterPushEventHandler ( name string , fn PushEventHandler , help HelpProvider ) { pluginHelp [ name ] = help pushEventHandlers [ name ] = fn }

// RegisterPushEventHandler registers a plugin's github.PushEvent handler.

RegisterPushEventHandler registers a plugin s github . PushEvent handler .


func RegisterReviewEventHandler(name string, fn ReviewEventHandler, help HelpProvider) {
	pluginHelp[name] = help
	reviewEventHandlers[name] = fn
}

func RegisterReviewEventHandler ( name string , fn ReviewEventHandler , help HelpProvider ) { pluginHelp [ name ] = help reviewEventHandlers [ name ] = fn }

// RegisterReviewEventHandler registers a plugin's github.ReviewEvent handler.

RegisterReviewEventHandler registers a plugin s github . ReviewEvent handler .


func RegisterReviewCommentEventHandler(name string, fn ReviewCommentEventHandler, help HelpProvider) {
	pluginHelp[name] = help
	reviewCommentEventHandlers[name] = fn
}

func RegisterReviewCommentEventHandler ( name string , fn ReviewCommentEventHandler , help HelpProvider ) { pluginHelp [ name ] = help reviewCommentEventHandlers [ name ] = fn }

// RegisterReviewCommentEventHandler registers a plugin's github.ReviewCommentEvent handler.

RegisterReviewCommentEventHandler registers a plugin s github . ReviewCommentEvent handler .


func RegisterGenericCommentHandler(name string, fn GenericCommentHandler, help HelpProvider) {
	pluginHelp[name] = help
	genericCommentHandlers[name] = fn
}

func RegisterGenericCommentHandler ( name string , fn GenericCommentHandler , help HelpProvider ) { pluginHelp [ name ] = help genericCommentHandlers [ name ] = fn }

// RegisterGenericCommentHandler registers a plugin's github.GenericCommentEvent handler.

RegisterGenericCommentHandler registers a plugin s github . GenericCommentEvent handler .


func NewAgent(configAgent *config.Agent, pluginConfigAgent *ConfigAgent, clientAgent *ClientAgent, logger *logrus.Entry) Agent {
	prowConfig := configAgent.Config()
	pluginConfig := pluginConfigAgent.Config()
	return Agent{
		GitHubClient:     clientAgent.GitHubClient,
		KubernetesClient: clientAgent.KubernetesClient,
		ProwJobClient:    clientAgent.ProwJobClient,
		GitClient:        clientAgent.GitClient,
		SlackClient:      clientAgent.SlackClient,
		OwnersClient:     clientAgent.OwnersClient,
		Config:           prowConfig,
		PluginConfig:     pluginConfig,
		Logger:           logger,
	}
}

func NewAgent ( configAgent * config . Agent , pluginConfigAgent * ConfigAgent , clientAgent * ClientAgent , logger * logrus . Entry ) Agent { prowConfig := configAgent . Config ( ) pluginConfig := pluginConfigAgent . Config ( ) return Agent { GitHubClient : clientAgent . GitHubClient , KubernetesClient : clientAgent . KubernetesClient , ProwJobClient : clientAgent . ProwJobClient , GitClient : clientAgent . GitClient , SlackClient : clientAgent . SlackClient , OwnersClient : clientAgent . OwnersClient , Config : prowConfig , PluginConfig : pluginConfig , Logger : logger , } }

// NewAgent bootstraps a new config.Agent struct from the passed dependencies.

NewAgent bootstraps a new config . Agent struct from the passed dependencies .


func (a *Agent) InitializeCommentPruner(org, repo string, pr int) {
	a.commentPruner = commentpruner.NewEventClient(
		a.GitHubClient, a.Logger.WithField("client", "commentpruner"),
		org, repo, pr,
	)
}

func ( a * Agent ) InitializeCommentPruner ( org , repo string , pr int ) { a . commentPruner = commentpruner . NewEventClient ( a . GitHubClient , a . Logger . WithField ( " " , " " ) , org , repo , pr , ) }

// InitializeCommentPruner attaches a commentpruner.EventClient to the agent to handle
// pruning comments.

InitializeCommentPruner attaches a commentpruner . EventClient to the agent to handle pruning comments .


func (a *Agent) CommentPruner() (*commentpruner.EventClient, error) {
	if a.commentPruner == nil {
		return nil, errors.New("comment pruner client never initialized")
	}
	return a.commentPruner, nil
}

func ( a * Agent ) CommentPruner ( ) ( * commentpruner . EventClient , error ) { if a . commentPruner == nil { return nil , errors . New ( " " ) } return a . commentPruner , nil }

// CommentPruner will return the commentpruner.EventClient attached to the agent or an error
// if one is not attached.

CommentPruner will return the commentpruner . EventClient attached to the agent or an error if one is not attached .


func (pa *ConfigAgent) Load(path string) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	np := &Configuration{}
	if err := yaml.Unmarshal(b, np); err != nil {
		return err
	}
	if err := np.Validate(); err != nil {
		return err
	}

	pa.Set(np)
	return nil
}

func ( pa * ConfigAgent ) Load ( path string ) error { b , err := ioutil . ReadFile ( path ) if err != nil { return err } np := & Configuration { } if err := yaml . Unmarshal ( b , np ) ; err != nil { return err } if err := np . Validate ( ) ; err != nil { return err } pa . Set ( np ) return nil }

// Load attempts to load config from the path. It returns an error if either
// the file can't be read or the configuration is invalid.

Load attempts to load config from the path . It returns an error if either the file can t be read or the configuration is invalid .


func (pa *ConfigAgent) Config() *Configuration {
	pa.mut.Lock()
	defer pa.mut.Unlock()
	return pa.configuration
}

func ( pa * ConfigAgent ) Config ( ) * Configuration { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) return pa . configuration }

// Config returns the agent current Configuration.

Config returns the agent current Configuration .


func (pa *ConfigAgent) Set(pc *Configuration) {
	pa.mut.Lock()
	defer pa.mut.Unlock()
	pa.configuration = pc
}

func ( pa * ConfigAgent ) Set ( pc * Configuration ) { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) pa . configuration = pc }

// Set attempts to set the plugins that are enabled on repos. Plugins are listed
// as a map from repositories to the list of plugins that are enabled on them.
// Specifying simply an org name will also work, and will enable the plugin on
// all repos in the org.

Set attempts to set the plugins that are enabled on repos . Plugins are listed as a map from repositories to the list of plugins that are enabled on them . Specifying simply an org name will also work and will enable the plugin on all repos in the org .


func (pa *ConfigAgent) Start(path string) error {
	if err := pa.Load(path); err != nil {
		return err
	}
	ticker := time.Tick(1 * time.Minute)
	go func() {
		for range ticker {
			if err := pa.Load(path); err != nil {
				logrus.WithField("path", path).WithError(err).Error("Error loading plugin config.")
			}
		}
	}()
	return nil
}

func ( pa * ConfigAgent ) Start ( path string ) error { if err := pa . Load ( path ) ; err != nil { return err } ticker := time . Tick ( 1 * time . Minute ) go func ( ) { for range ticker { if err := pa . Load ( path ) ; err != nil { logrus . WithField ( " " , path ) . WithError ( err ) . Error ( " " ) } } } ( ) return nil }

// Start starts polling path for plugin config. If the first attempt fails,
// then start returns the error. Future errors will halt updates but not stop.

Start starts polling path for plugin config . If the first attempt fails then start returns the error . Future errors will halt updates but not stop .


func (pa *ConfigAgent) GenericCommentHandlers(owner, repo string) map[string]GenericCommentHandler {
	pa.mut.Lock()
	defer pa.mut.Unlock()

	hs := map[string]GenericCommentHandler{}
	for _, p := range pa.getPlugins(owner, repo) {
		if h, ok := genericCommentHandlers[p]; ok {
			hs[p] = h
		}
	}
	return hs
}

func ( pa * ConfigAgent ) GenericCommentHandlers ( owner , repo string ) map [ string ] GenericCommentHandler { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) hs := map [ string ] GenericCommentHandler { } for _ , p := range pa . getPlugins ( owner , repo ) { if h , ok := genericCommentHandlers [ p ] ; ok { hs [ p ] = h } } return hs }

// GenericCommentHandlers returns a map of plugin names to handlers for the repo.

GenericCommentHandlers returns a map of plugin names to handlers for the repo .


func (pa *ConfigAgent) IssueHandlers(owner, repo string) map[string]IssueHandler {
	pa.mut.Lock()
	defer pa.mut.Unlock()

	hs := map[string]IssueHandler{}
	for _, p := range pa.getPlugins(owner, repo) {
		if h, ok := issueHandlers[p]; ok {
			hs[p] = h
		}
	}
	return hs
}

func ( pa * ConfigAgent ) IssueHandlers ( owner , repo string ) map [ string ] IssueHandler { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) hs := map [ string ] IssueHandler { } for _ , p := range pa . getPlugins ( owner , repo ) { if h , ok := issueHandlers [ p ] ; ok { hs [ p ] = h } } return hs }

// IssueHandlers returns a map of plugin names to handlers for the repo.

IssueHandlers returns a map of plugin names to handlers for the repo .


func (pa *ConfigAgent) IssueCommentHandlers(owner, repo string) map[string]IssueCommentHandler {
	pa.mut.Lock()
	defer pa.mut.Unlock()

	hs := map[string]IssueCommentHandler{}
	for _, p := range pa.getPlugins(owner, repo) {
		if h, ok := issueCommentHandlers[p]; ok {
			hs[p] = h
		}
	}

	return hs
}

func ( pa * ConfigAgent ) IssueCommentHandlers ( owner , repo string ) map [ string ] IssueCommentHandler { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) hs := map [ string ] IssueCommentHandler { } for _ , p := range pa . getPlugins ( owner , repo ) { if h , ok := issueCommentHandlers [ p ] ; ok { hs [ p ] = h } } return hs }

// IssueCommentHandlers returns a map of plugin names to handlers for the repo.

IssueCommentHandlers returns a map of plugin names to handlers for the repo .


func (pa *ConfigAgent) PullRequestHandlers(owner, repo string) map[string]PullRequestHandler {
	pa.mut.Lock()
	defer pa.mut.Unlock()

	hs := map[string]PullRequestHandler{}
	for _, p := range pa.getPlugins(owner, repo) {
		if h, ok := pullRequestHandlers[p]; ok {
			hs[p] = h
		}
	}

	return hs
}

func ( pa * ConfigAgent ) PullRequestHandlers ( owner , repo string ) map [ string ] PullRequestHandler { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) hs := map [ string ] PullRequestHandler { } for _ , p := range pa . getPlugins ( owner , repo ) { if h , ok := pullRequestHandlers [ p ] ; ok { hs [ p ] = h } } return hs }

// PullRequestHandlers returns a map of plugin names to handlers for the repo.

PullRequestHandlers returns a map of plugin names to handlers for the repo .


func (pa *ConfigAgent) ReviewEventHandlers(owner, repo string) map[string]ReviewEventHandler {
	pa.mut.Lock()
	defer pa.mut.Unlock()

	hs := map[string]ReviewEventHandler{}
	for _, p := range pa.getPlugins(owner, repo) {
		if h, ok := reviewEventHandlers[p]; ok {
			hs[p] = h
		}
	}

	return hs
}

func ( pa * ConfigAgent ) ReviewEventHandlers ( owner , repo string ) map [ string ] ReviewEventHandler { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) hs := map [ string ] ReviewEventHandler { } for _ , p := range pa . getPlugins ( owner , repo ) { if h , ok := reviewEventHandlers [ p ] ; ok { hs [ p ] = h } } return hs }

// ReviewEventHandlers returns a map of plugin names to handlers for the repo.

ReviewEventHandlers returns a map of plugin names to handlers for the repo .


func (pa *ConfigAgent) ReviewCommentEventHandlers(owner, repo string) map[string]ReviewCommentEventHandler {
	pa.mut.Lock()
	defer pa.mut.Unlock()

	hs := map[string]ReviewCommentEventHandler{}
	for _, p := range pa.getPlugins(owner, repo) {
		if h, ok := reviewCommentEventHandlers[p]; ok {
			hs[p] = h
		}
	}

	return hs
}

func ( pa * ConfigAgent ) ReviewCommentEventHandlers ( owner , repo string ) map [ string ] ReviewCommentEventHandler { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) hs := map [ string ] ReviewCommentEventHandler { } for _ , p := range pa . getPlugins ( owner , repo ) { if h , ok := reviewCommentEventHandlers [ p ] ; ok { hs [ p ] = h } } return hs }

// ReviewCommentEventHandlers returns a map of plugin names to handlers for the repo.

ReviewCommentEventHandlers returns a map of plugin names to handlers for the repo .


func (pa *ConfigAgent) StatusEventHandlers(owner, repo string) map[string]StatusEventHandler {
	pa.mut.Lock()
	defer pa.mut.Unlock()

	hs := map[string]StatusEventHandler{}
	for _, p := range pa.getPlugins(owner, repo) {
		if h, ok := statusEventHandlers[p]; ok {
			hs[p] = h
		}
	}

	return hs
}

func ( pa * ConfigAgent ) StatusEventHandlers ( owner , repo string ) map [ string ] StatusEventHandler { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) hs := map [ string ] StatusEventHandler { } for _ , p := range pa . getPlugins ( owner , repo ) { if h , ok := statusEventHandlers [ p ] ; ok { hs [ p ] = h } } return hs }

// StatusEventHandlers returns a map of plugin names to handlers for the repo.

StatusEventHandlers returns a map of plugin names to handlers for the repo .


func (pa *ConfigAgent) PushEventHandlers(owner, repo string) map[string]PushEventHandler {
	pa.mut.Lock()
	defer pa.mut.Unlock()

	hs := map[string]PushEventHandler{}
	for _, p := range pa.getPlugins(owner, repo) {
		if h, ok := pushEventHandlers[p]; ok {
			hs[p] = h
		}
	}

	return hs
}

func ( pa * ConfigAgent ) PushEventHandlers ( owner , repo string ) map [ string ] PushEventHandler { pa . mut . Lock ( ) defer pa . mut . Unlock ( ) hs := map [ string ] PushEventHandler { } for _ , p := range pa . getPlugins ( owner , repo ) { if h , ok := pushEventHandlers [ p ] ; ok { hs [ p ] = h } } return hs }

// PushEventHandlers returns a map of plugin names to handlers for the repo.

PushEventHandlers returns a map of plugin names to handlers for the repo .


func EventsForPlugin(name string) []string {
	var events []string
	if _, ok := issueHandlers[name]; ok {
		events = append(events, "issue")
	}
	if _, ok := issueCommentHandlers[name]; ok {
		events = append(events, "issue_comment")
	}
	if _, ok := pullRequestHandlers[name]; ok {
		events = append(events, "pull_request")
	}
	if _, ok := pushEventHandlers[name]; ok {
		events = append(events, "push")
	}
	if _, ok := reviewEventHandlers[name]; ok {
		events = append(events, "pull_request_review")
	}
	if _, ok := reviewCommentEventHandlers[name]; ok {
		events = append(events, "pull_request_review_comment")
	}
	if _, ok := statusEventHandlers[name]; ok {
		events = append(events, "status")
	}
	if _, ok := genericCommentHandlers[name]; ok {
		events = append(events, "GenericCommentEvent (any event for user text)")
	}
	return events
}

func EventsForPlugin ( name string ) [ ] string { var events [ ] string if _ , ok := issueHandlers [ name ] ; ok { events = append ( events , " " ) } if _ , ok := issueCommentHandlers [ name ] ; ok { events = append ( events , " " ) } if _ , ok := pullRequestHandlers [ name ] ; ok { events = append ( events , " " ) } if _ , ok := pushEventHandlers [ name ] ; ok { events = append ( events , " " ) } if _ , ok := reviewEventHandlers [ name ] ; ok { events = append ( events , " " ) } if _ , ok := reviewCommentEventHandlers [ name ] ; ok { events = append ( events , " " ) } if _ , ok := statusEventHandlers [ name ] ; ok { events = append ( events , " " ) } if _ , ok := genericCommentHandlers [ name ] ; ok { events = append ( events , " " ) } return events }

// EventsForPlugin returns the registered events for the passed plugin.

EventsForPlugin returns the registered events for the passed plugin .


func insertLink(started *gcs.Started, viewURL string) (bool, error) {
	if started.Metadata == nil {
		started.Metadata = metadata.Metadata{}
	}
	meta := started.Metadata
	var changed bool
	top, present := meta.String(resultstoreKey)
	if !present || top == nil || *top != viewURL {
		changed = true
		meta[resultstoreKey] = viewURL
	}
	links, present := meta.Meta(linksKey)
	if present && links == nil {
		return false, fmt.Errorf("metadata.links is not a Metadata value: %v", meta[linksKey])
	}
	if links == nil {
		links = &metadata.Metadata{}
		changed = true
	}
	resultstoreMeta, present := links.Meta(resultstoreKey)
	if present && resultstoreMeta == nil {
		return false, fmt.Errorf("metadata.links.resultstore is not a Metadata value: %v", (*links)[resultstoreKey])
	}
	if resultstoreMeta == nil {
		resultstoreMeta = &metadata.Metadata{}
		changed = true
	}
	val, present := resultstoreMeta.String(urlKey)
	if present && val == nil {
		return false, fmt.Errorf("metadata.links.resultstore.url is not a string value: %v", (*resultstoreMeta)[urlKey])
	}
	if !changed && val != nil && *val == viewURL {
		return false, nil
	}

	(*resultstoreMeta)[urlKey] = viewURL
	(*links)[resultstoreKey] = *resultstoreMeta
	meta[linksKey] = *links
	return true, nil
}

func insertLink ( started * gcs . Started , viewURL string ) ( bool , error ) { if started . Metadata == nil { started . Metadata = metadata . Metadata { } } meta := started . Metadata var changed bool top , present := meta . String ( resultstoreKey ) if ! present || top == nil || * top != viewURL { changed = true meta [ resultstoreKey ] = viewURL } links , present := meta . Meta ( linksKey ) if present && links == nil { return false , fmt . Errorf ( " " , meta [ linksKey ] ) } if links == nil { links = & metadata . Metadata { } changed = true } resultstoreMeta , present := links . Meta ( resultstoreKey ) if present && resultstoreMeta == nil { return false , fmt . Errorf ( " " , ( * links ) [ resultstoreKey ] ) } if resultstoreMeta == nil { resultstoreMeta = & metadata . Metadata { } changed = true } val , present := resultstoreMeta . String ( urlKey ) if present && val == nil { return false , fmt . Errorf ( " " , ( * resultstoreMeta ) [ urlKey ] ) } if ! changed && val != nil && * val == viewURL { return false , nil } ( * resultstoreMeta ) [ urlKey ] = viewURL ( * links ) [ resultstoreKey ] = * resultstoreMeta meta [ linksKey ] = * links return true , nil }

// insertLink attempts to set metadata.links.resultstore.url to viewURL.
//
// returns true if started metadata was updated.

insertLink attempts to set metadata . links . resultstore . url to viewURL . returns true if started metadata was updated .


func HelpProvider(enabledRepos []string) (*pluginhelp.PluginHelp, error) {
	pluginHelp := &pluginhelp.PluginHelp{
		Description: `The cherrypick plugin is used for cherrypicking PRs across branches. For every successful cherrypick invocation a new PR is opened against the target branch and assigned to the requester. If the parent PR contains a release note, it is copied to the cherrypick PR.`,
	}
	pluginHelp.AddCommand(pluginhelp.Command{
		Usage:       "/cherrypick [branch]",
		Description: "Cherrypick a PR to a different branch. This command works both in merged PRs (the cherrypick PR is opened immediately) and open PRs (the cherrypick PR opens as soon as the original PR merges).",
		Featured:    true,
		// depends on how the cherrypick server runs; needs auth by default (--allow-all=false)
		WhoCanUse: "Members of the trusted organization for the repo.",
		Examples:  []string{"/cherrypick release-3.9"},
	})
	return pluginHelp, nil
}

// HelpProvider construct the pluginhelp.PluginHelp for this plugin.

HelpProvider construct the pluginhelp . PluginHelp for this plugin .


func (s *Server) getPatch(org, repo, targetBranch string, num int) (string, error) {
	patch, err := s.ghc.GetPullRequestPatch(org, repo, num)
	if err != nil {
		return "", err
	}
	localPath := fmt.Sprintf("/tmp/%s_%s_%d_%s.patch", org, repo, num, normalize(targetBranch))
	out, err := os.Create(localPath)
	if err != nil {
		return "", err
	}
	defer out.Close()
	if _, err := io.Copy(out, bytes.NewBuffer(patch)); err != nil {
		return "", err
	}
	return localPath, nil
}

func ( s * Server ) getPatch ( org , repo , targetBranch string , num int ) ( string , error ) { patch , err := s . ghc . GetPullRequestPatch ( org , repo , num ) if err != nil { return " " , err } localPath := fmt . Sprintf ( " " , org , repo , num , normalize ( targetBranch ) ) out , err := os . Create ( localPath ) if err != nil { return " " , err } defer out . Close ( ) if _ , err := io . Copy ( out , bytes . NewBuffer ( patch ) ) ; err != nil { return " " , err } return localPath , nil }

// getPatch gets the patch for the provided PR and creates a local
// copy of it. It returns its location in the filesystem and any
// encountered error.

getPatch gets the patch for the provided PR and creates a local copy of it . It returns its location in the filesystem and any encountered error .


func releaseNoteFromParentPR(body string) string {
	potentialMatch := releaseNoteRe.FindStringSubmatch(body)
	if potentialMatch == nil {
		return ""
	}
	return fmt.Sprintf("```release-note\n%s\n```", strings.TrimSpace(potentialMatch[1]))
}

func releaseNoteFromParentPR ( body string ) string { potentialMatch := releaseNoteRe . FindStringSubmatch ( body ) if potentialMatch == nil { return " " } return fmt . Sprintf ( " \n \n " , strings . TrimSpace ( potentialMatch [ 1 ] ) ) }

// releaseNoteNoteFromParentPR gets the release note from the
// parent PR and formats it as per the PR template so that
// it can be copied to the cherry-pick PR.

releaseNoteNoteFromParentPR gets the release note from the parent PR and formats it as per the PR template so that it can be copied to the cherry - pick PR .


func ValidatePayload(payload []byte, sig string, key []byte) bool {
	if !strings.HasPrefix(sig, "sha1=") {
		return false
	}
	sig = sig[5:]
	sb, err := hex.DecodeString(sig)
	if err != nil {
		return false
	}
	mac := hmac.New(sha1.New, key)
	mac.Write(payload)
	expected := mac.Sum(nil)
	return hmac.Equal(sb, expected)
}

func ValidatePayload ( payload [ ] byte , sig string , key [ ] byte ) bool { if ! strings . HasPrefix ( sig , " " ) { return false } sig = sig [ 5 : ] sb , err := hex . DecodeString ( sig ) if err != nil { return false } mac := hmac . New ( sha1 . New , key ) mac . Write ( payload ) expected := mac . Sum ( nil ) return hmac . Equal ( sb , expected ) }

// ValidatePayload ensures that the request payload signature matches the key.

ValidatePayload ensures that the request payload signature matches the key .


func PayloadSignature(payload []byte, key []byte) string {
	mac := hmac.New(sha1.New, key)
	mac.Write(payload)
	sum := mac.Sum(nil)
	return "sha1=" + hex.EncodeToString(sum)
}

func PayloadSignature ( payload [ ] byte , key [ ] byte ) string { mac := hmac . New ( sha1 . New , key ) mac . Write ( payload ) sum := mac . Sum ( nil ) return " " + hex . EncodeToString ( sum ) }

// PayloadSignature returns the signature that matches the payload.

PayloadSignature returns the signature that matches the payload .


func updateString(have, want *string) bool {
	switch {
	case have == nil:
		panic("have must be non-nil")
	case want == nil:
		return false // do not care what we have
	case *have == *want:
		return false // already have it
	}
	*have = *want // update value
	return true
}

func
updateString
(
have
,
want
*
string
)
bool
{
switch
{
case
have
==
:
panic
(
"
"
)
case
want
==
:
return
false
// do not care what we have
case
*
have
==
*
want
:
return
false
// already have it
}
*
have
=
*
want
// update value
return
true
}

// updateString will return true and set have to want iff they are set and different.

updateString will return true and set have to want iff they are set and different .


func updateBool(have, want *bool) bool {
	switch {
	case have == nil:
		panic("have must not be nil")
	case want == nil:
		return false // do not care what we have
	case *have == *want:
		return false //already have it
	}
	*have = *want // update value
	return true
}

func
updateBool
(
have
,
want
*
bool
)
bool
{
switch
{
case
have
==
:
panic
(
"
"
)
case
want
==
:
return
false
// do not care what we have
case
*
have
==
*
want
:
return
false
//already have it
}
*
have
=
*
want
// update value
return
true
}

// updateBool will return true and set have to want iff they are set and different.

updateBool will return true and set have to want iff they are set and different .


func configureOrgMeta(client orgMetadataClient, orgName string, want org.Metadata) error {
	cur, err := client.GetOrg(orgName)
	if err != nil {
		return fmt.Errorf("failed to get %s metadata: %v", orgName, err)
	}
	change := false
	change = updateString(&cur.BillingEmail, want.BillingEmail) || change
	change = updateString(&cur.Company, want.Company) || change
	change = updateString(&cur.Email, want.Email) || change
	change = updateString(&cur.Name, want.Name) || change
	change = updateString(&cur.Description, want.Description) || change
	change = updateString(&cur.Location, want.Location) || change
	if want.DefaultRepositoryPermission != nil {
		w := string(*want.DefaultRepositoryPermission)
		change = updateString(&cur.DefaultRepositoryPermission, &w)
	}
	change = updateBool(&cur.HasOrganizationProjects, want.HasOrganizationProjects) || change
	change = updateBool(&cur.HasRepositoryProjects, want.HasRepositoryProjects) || change
	change = updateBool(&cur.MembersCanCreateRepositories, want.MembersCanCreateRepositories) || change
	if change {
		if _, err := client.EditOrg(orgName, *cur); err != nil {
			return fmt.Errorf("failed to edit %s metadata: %v", orgName, err)
		}
	}
	return nil
}

func configureOrgMeta ( client orgMetadataClient , orgName string , want org . Metadata ) error { cur , err := client . GetOrg ( orgName ) if err != nil { return fmt . Errorf ( " " , orgName , err ) } change := false change = updateString ( & cur . BillingEmail , want . BillingEmail ) || change change = updateString ( & cur . Company , want . Company ) || change change = updateString ( & cur . Email , want . Email ) || change change = updateString ( & cur . Name , want . Name ) || change change = updateString ( & cur . Description , want . Description ) || change change = updateString ( & cur . Location , want . Location ) || change if want . DefaultRepositoryPermission != nil { w := string ( * want . DefaultRepositoryPermission ) change = updateString ( & cur . DefaultRepositoryPermission , & w ) } change = updateBool ( & cur . HasOrganizationProjects , want . HasOrganizationProjects ) || change change = updateBool ( & cur . HasRepositoryProjects , want . HasRepositoryProjects ) || change change = updateBool ( & cur . MembersCanCreateRepositories , want . MembersCanCreateRepositories ) || change if change { if _ , err := client . EditOrg ( orgName , * cur ) ; err != nil { return fmt . Errorf ( " " , orgName , err ) } } return nil }

// configureOrgMeta will update github to have the non-nil wanted metadata values.

configureOrgMeta will update github to have the non - nil wanted metadata values .


func configureTeamRepos(client teamRepoClient, githubTeams map[string]github.Team, name, orgName string, team org.Team) error {
	gt, ok := githubTeams[name]
	if !ok { // configureTeams is buggy if this is the case
		return fmt.Errorf("%s not found in id list", name)
	}

	want := team.Repos
	have := map[string]github.RepoPermissionLevel{}
	repos, err := client.ListTeamRepos(gt.ID)
	if err != nil {
		return fmt.Errorf("failed to list team %d(%s) repos: %v", gt.ID, name, err)
	}
	for _, repo := range repos {
		have[repo.Name] = github.LevelFromPermissions(repo.Permissions)
	}

	actions := map[string]github.RepoPermissionLevel{}
	for wantRepo, wantPermission := range want {
		if havePermission, haveRepo := have[wantRepo]; haveRepo && havePermission == wantPermission {
			// nothing to do
			continue
		}
		// create or update this permission
		actions[wantRepo] = wantPermission
	}

	for haveRepo := range have {
		if _, wantRepo := want[haveRepo]; !wantRepo {
			// should remove these permissions
			actions[haveRepo] = github.None
		}
	}

	var updateErrors []error
	for repo, permission := range actions {
		var err error
		if permission == github.None {
			err = client.RemoveTeamRepo(gt.ID, orgName, repo)
		} else {
			err = client.UpdateTeamRepo(gt.ID, orgName, repo, permission)
		}
		if err != nil {
			updateErrors = append(updateErrors, fmt.Errorf("failed to update team %d(%s) permissions on repo %s to %s: %v", gt.ID, name, repo, permission, err))
		}
	}

	return errorutil.NewAggregate(updateErrors...)
}

// configureTeamRepos updates the list of repos that the team has permissions for when necessary


func (c *Client) ShouldReport(pj *prowapi.ProwJob) bool {
	pubSubMap := findLabels(pj, PubSubProjectLabel, PubSubTopicLabel)
	return pubSubMap[PubSubProjectLabel] != "" && pubSubMap[PubSubTopicLabel] != ""
}

func ( c * Client ) ShouldReport ( pj * prowapi . ProwJob ) bool { pubSubMap := findLabels ( pj , PubSubProjectLabel , PubSubTopicLabel ) return pubSubMap [ PubSubProjectLabel ] != " " && pubSubMap [ PubSubTopicLabel ] != " " }

// ShouldReport tells if a prowjob should be reported by this reporter


func (o Options) Run(spec *downwardapi.JobSpec, extra map[string]gcs.UploadFunc) error {
	uploadTargets := o.assembleTargets(spec, extra)

	if !o.DryRun {
		ctx := context.Background()
		gcsClient, err := storage.NewClient(ctx, option.WithCredentialsFile(o.GcsCredentialsFile))
		if err != nil {
			return fmt.Errorf("could not connect to GCS: %v", err)
		}

		if err := gcs.Upload(gcsClient.Bucket(o.Bucket), uploadTargets); err != nil {
			return fmt.Errorf("failed to upload to GCS: %v", err)
		}
	} else {
		for destination := range uploadTargets {
			logrus.WithField("dest", destination).Info("Would upload")
		}
	}

	logrus.Info("Finished upload to GCS")
	return nil
}

func ( o Options ) Run ( spec * downwardapi . JobSpec , extra map [ string ] gcs . UploadFunc ) error { uploadTargets := o . assembleTargets ( spec , extra ) if ! o . DryRun { ctx := context . Background ( ) gcsClient , err := storage . NewClient ( ctx , option . WithCredentialsFile ( o . GcsCredentialsFile ) ) if err != nil { return fmt . Errorf ( " " , err ) } if err := gcs . Upload ( gcsClient . Bucket ( o . Bucket ) , uploadTargets ) ; err != nil { return fmt . Errorf ( " " , err ) } } else { for destination := range uploadTargets { logrus . WithField ( " " , destination ) . Info ( " " ) } } logrus . Info ( " " ) return nil }

// Run will upload files to GCS as prescribed by
// the options. Any extra files can be passed as
// a parameter and will have the prefix prepended
// to their destination in GCS, so the caller can
// operate relative to the base of the GCS dir.

Run will upload files to GCS as prescribed by the options . Any extra files can be passed as a parameter and will have the prefix prepended to their destination in GCS so the caller can operate relative to the base of the GCS dir .


func (d *DefaultFieldsFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	data := make(logrus.Fields, len(entry.Data)+len(d.DefaultFields))
	for k, v := range d.DefaultFields {
		data[k] = v
	}
	for k, v := range entry.Data {
		data[k] = v
	}
	return d.WrappedFormatter.Format(&logrus.Entry{
		Logger:  entry.Logger,
		Data:    data,
		Time:    entry.Time,
		Level:   entry.Level,
		Message: entry.Message,
	})
}

func ( d * DefaultFieldsFormatter ) Format ( entry * logrus . Entry ) ( [ ] byte , error ) { data := make ( logrus . Fields , len ( entry . Data ) + len ( d . DefaultFields ) ) for k , v := range d . DefaultFields { data [ k ] = v } for k , v := range entry . Data { data [ k ] = v } return d . WrappedFormatter . Format ( & logrus . Entry { Logger : entry . Logger , Data : data , Time : entry . Time , Level : entry . Level , Message : entry . Message , } ) }

// Format implements logrus.Formatter's Format. We allocate a new Fields
// map in order to not modify the caller's Entry, as that is not a thread
// safe operation.

Format implements logrus . Formatter s Format . We allocate a new Fields map in order to not modify the caller s Entry as that is not a thread safe operation .


func (issue *Issue) FindLabels(regex *regexp.Regexp) []Label {
	labels := []Label{}

	for _, label := range issue.Labels {
		if regex.MatchString(label.Name) {
			labels = append(labels, label)
		}
	}

	return labels
}

func ( issue * Issue ) FindLabels ( regex * regexp . Regexp ) [ ] Label { labels := [ ] Label { } for _ , label := range issue . Labels { if regex . MatchString ( label . Name ) { labels = append ( labels , label ) } } return labels }

// FindLabels returns the list of labels matching the regex


func (o *Options) AddFlags(flags *flag.FlagSet) {
	flags.StringVar(&o.Log, "clone-log", "", "Path to the clone records log")
	o.Options.AddFlags(flags)
}

func ( o * Options ) AddFlags ( flags * flag . FlagSet ) { flags . StringVar ( & o . Log , " " , " " , " " ) o . Options . AddFlags ( flags ) }


func NewAgent(config *config.GitHubOAuthConfig, logger *logrus.Entry) *Agent {
	return &Agent{
		gc:     config,
		logger: logger,
	}
}

func NewAgent ( config * config . GitHubOAuthConfig , logger * logrus . Entry ) * Agent { return & Agent { gc : config , logger : logger , } }

// NewAgent returns a new GitHub OAuth Agent.

NewAgent returns a new GitHub OAuth Agent .


func (ga *Agent) HandleLogin(client OAuthClient) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		stateToken := xsrftoken.Generate(ga.gc.ClientSecret, "", "")
		state := hex.EncodeToString([]byte(stateToken))
		oauthSession, err := ga.gc.CookieStore.New(r, oauthSessionCookie)
		oauthSession.Options.Secure = true
		oauthSession.Options.HttpOnly = true
		if err != nil {
			ga.serverError(w, "Creating new OAuth session", err)
			return
		}
		oauthSession.Options.MaxAge = 10 * 60
		oauthSession.Values[stateKey] = state

		if err := oauthSession.Save(r, w); err != nil {
			ga.serverError(w, "Save oauth session", err)
			return
		}

		redirectURL := client.AuthCodeURL(state, oauth2.ApprovalForce, oauth2.AccessTypeOnline)
		http.Redirect(w, r, redirectURL, http.StatusFound)
	}
}

func ( ga * Agent ) HandleLogin ( client OAuthClient ) http . HandlerFunc { return func ( w http . ResponseWriter , r * http . Request ) { stateToken := xsrftoken . Generate ( ga . gc . ClientSecret , " " , " " ) state := hex . EncodeToString ( [ ] byte ( stateToken ) ) oauthSession , err := ga . gc . CookieStore . New ( r , oauthSessionCookie ) oauthSession . Options . Secure = true oauthSession . Options . HttpOnly = true if err != nil { ga . serverError ( w , " " , err ) return } oauthSession . Options . MaxAge = 10 * 60 oauthSession . Values [ stateKey ] = state if err := oauthSession . Save ( r , w ) ; err != nil { ga . serverError ( w , " " , err ) return } redirectURL := client . AuthCodeURL ( state , oauth2 . ApprovalForce , oauth2 . AccessTypeOnline ) http . Redirect ( w , r , redirectURL , http . StatusFound ) } }

// HandleLogin handles GitHub login request from front-end. It starts a new git oauth session and
// redirect user to GitHub OAuth end-point for authentication.

HandleLogin handles GitHub login request from front - end . It starts a new git oauth session and redirect user to GitHub OAuth end - point for authentication .


func (ga *Agent) HandleLogout(client OAuthClient) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		accessTokenSession, err := ga.gc.CookieStore.Get(r, tokenSession)
		if err != nil {
			ga.serverError(w, "get cookie", err)
			return
		}
		// Clear session
		accessTokenSession.Options.MaxAge = -1
		if err := accessTokenSession.Save(r, w); err != nil {
			ga.serverError(w, "Save invalidated session on log out", err)
			return
		}
		loginCookie, err := r.Cookie(loginSession)
		if err == nil {
			loginCookie.MaxAge = -1
			loginCookie.Expires = time.Now().Add(-time.Hour * 24)
			http.SetCookie(w, loginCookie)
		}
		http.Redirect(w, r, ga.gc.FinalRedirectURL, http.StatusFound)
	}
}

func
(
ga
*
Agent
)
HandleLogout
(
client
OAuthClient
)
http
.
HandlerFunc
{
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
accessTokenSession
,
err
:=
ga
.
gc
.
CookieStore
.
Get
(
r
,
tokenSession
)
if
err
!=
{
ga
.
serverError
(
w
,
"
"
,
err
)
return
}
// Clear session
accessTokenSession
.
Options
.
MaxAge
=
1
if
err
:=
accessTokenSession
.
Save
(
r
,
w
)
;
err
!=
{
ga
.
serverError
(
w
,
"
"
,
err
)
return
}
loginCookie
,
err
:=
r
.
Cookie
(
loginSession
)
if
err
==
{
loginCookie
.
MaxAge
=
1
loginCookie
.
Expires
=
time
.
Now
(
)
.
Add
(
time
.
Hour
*
24
)
http
.
SetCookie
(
w
,
loginCookie
)
}
http
.
Redirect
(
w
,
r
,
ga
.
gc
.
FinalRedirectURL
,
http
.
StatusFound
)
}
}

// HandleLogout handles GitHub logout request from front-end. It invalidates cookie sessions and
// redirect back to the front page.

HandleLogout handles GitHub logout request from front - end . It invalidates cookie sessions and redirect back to the front page .


func (ga *Agent) serverError(w http.ResponseWriter, action string, err error) {
	ga.logger.WithError(err).Errorf("Error %s.", action)
	msg := fmt.Sprintf("500 Internal server error %s: %v", action, err)
	http.Error(w, msg, http.StatusInternalServerError)
}

func ( ga * Agent ) serverError ( w http . ResponseWriter , action string , err error ) { ga . logger . WithError ( err ) . Errorf ( " " , action ) msg := fmt . Sprintf ( " " , action , err ) http . Error ( w , msg , http . StatusInternalServerError ) }


func (in *ResourcesConfigObject) FromItem(i common.Item) {
	c, err := common.ItemToResourcesConfig(i)
	if err == nil {
		in.fromConfig(c)
	}
}

func ( in * ResourcesConfigObject ) FromItem ( i common . Item ) { c , err := common . ItemToResourcesConfig ( i ) if err == nil { in . fromConfig ( c ) } }

// FromItem implements the Object interface


func (in *ResourcesConfigCollection) GetItems() []Object {
	var items []Object
	for _, i := range in.Items {
		items = append(items, i)
	}
	return items
}

func ( in * ResourcesConfigCollection ) GetItems ( ) [ ] Object { var items [ ] Object for _ , i := range in . Items { items = append ( items , i ) } return items }

// GetItems implements the Collection interface


func (in *ResourcesConfigCollection) SetItems(objects []Object) {
	var items []*ResourcesConfigObject
	for _, b := range objects {
		items = append(items, b.(*ResourcesConfigObject))
	}
	in.Items = items
}

func ( in * ResourcesConfigCollection ) SetItems ( objects [ ] Object ) { var items [ ] * ResourcesConfigObject for _ , b := range objects { items = append ( items , b . ( * ResourcesConfigObject ) ) } in . Items = items }

// SetItems implements the Collection interface


func (l *RepoPermissionLevel) UnmarshalText(text []byte) error {
	v := RepoPermissionLevel(text)
	if _, ok := repoPermissionLevels[v]; !ok {
		return fmt.Errorf("bad repo permission: %s not in %v", v, repoPermissionLevels)
	}
	*l = v
	return nil
}

func ( l * RepoPermissionLevel ) UnmarshalText ( text [ ] byte ) error { v := RepoPermissionLevel ( text ) if _ , ok := repoPermissionLevels [ v ] ; ! ok { return fmt . Errorf ( " " , v , repoPermissionLevels ) } * l = v return nil }

// UnmarshalText validates the text is a valid string


func (i Issue) IsAssignee(login string) bool {
	for _, assignee := range i.Assignees {
		if NormLogin(login) == NormLogin(assignee.Login) {
			return true
		}
	}
	return false
}

func ( i Issue ) IsAssignee ( login string ) bool { for _ , assignee := range i . Assignees { if NormLogin ( login ) == NormLogin ( assignee . Login ) { return true } } return false }

// IsAssignee checks if a user is assigned to the issue.

IsAssignee checks if a user is assigned to the issue .


func (i Issue) IsAuthor(login string) bool {
	return NormLogin(i.User.Login) == NormLogin(login)
}

func ( i Issue ) IsAuthor ( login string ) bool { return NormLogin ( i . User . Login ) == NormLogin ( login ) }

// IsAuthor checks if a user is the author of the issue.

IsAuthor checks if a user is the author of the issue .


func (i Issue) HasLabel(labelToFind string) bool {
	for _, label := range i.Labels {
		if strings.ToLower(label.Name) == strings.ToLower(labelToFind) {
			return true
		}
	}
	return false
}

func ( i Issue ) HasLabel ( labelToFind string ) bool { for _ , label := range i . Labels { if strings . ToLower ( label . Name ) == strings . ToLower ( labelToFind ) { return true } } return false }

// HasLabel checks if an issue has a given label.

HasLabel checks if an issue has a given label .


func (pe PushEvent) Branch() string {
	ref := strings.TrimPrefix(pe.Ref, "refs/heads/") // if Ref is a branch
	ref = strings.TrimPrefix(ref, "refs/tags/")      // if Ref is a tag
	return ref
}

func
(
pe
PushEvent
)
Branch
(
)
string
{
ref
:=
strings
.
TrimPrefix
(
pe
.
Ref
,
"
"
)
// if Ref is a branch
ref
=
strings
.
TrimPrefix
(
ref
,
"
"
)
// if Ref is a tag
return
ref
}

// Branch returns the name of the branch to which the user pushed.

Branch returns the name of the branch to which the user pushed .


func truncate(in string) string {
	const (
		half = (maxLen - len(elide)) / 2
	)
	if len(in) <= maxLen {
		return in
	}
	return in[:half] + elide + in[len(in)-half:]
}

func truncate ( in string ) string { const ( half = ( maxLen - len ( elide ) ) / 2 ) if len ( in ) <= maxLen { return in } return in [ : half ] + elide + in [ len ( in ) - half : ] }

// truncate converts "really long messages" into "really ... messages".

truncate converts really long messages into really ... messages .


func reportStatus(ghc GitHubClient, pj prowapi.ProwJob) error {
	refs := pj.Spec.Refs
	if pj.Spec.Report {
		contextState, err := prowjobStateToGitHubStatus(pj.Status.State)
		if err != nil {
			return err
		}
		sha := refs.BaseSHA
		if len(refs.Pulls) > 0 {
			sha = refs.Pulls[0].SHA
		}
		if err := ghc.CreateStatus(refs.Org, refs.Repo, sha, github.Status{
			State:       contextState,
			Description: truncate(pj.Status.Description),
			Context:     pj.Spec.Context, // consider truncating this too
			TargetURL:   pj.Status.URL,
		}); err != nil {
			return err
		}
	}
	return nil
}

func
reportStatus
(
ghc
GitHubClient
,
pj
prowapi
.
ProwJob
)
error
{
refs
:=
pj
.
Spec
.
Refs
if
pj
.
Spec
.
Report
{
contextState
,
err
:=
prowjobStateToGitHubStatus
(
pj
.
Status
.
State
)
if
err
!=
{
return
err
}
sha
:=
refs
.
BaseSHA
if
len
(
refs
.
Pulls
)
>
0
{
sha
=
refs
.
Pulls
[
0
]
.
SHA
}
if
err
:=
ghc
.
CreateStatus
(
refs
.
Org
,
refs
.
Repo
,
sha
,
github
.
Status
{
State
:
contextState
,
Description
:
truncate
(
pj
.
Status
.
Description
)
,
Context
:
pj
.
Spec
.
Context
,
// consider truncating this too
TargetURL
:
pj
.
Status
.
URL
,
}
)
;
err
!=
{
return
err
}
}
return
}

// reportStatus should be called on any prowjob status changes


func parseIssueComments(pj prowapi.ProwJob, botName string, ics []github.IssueComment) ([]int, []string, int) {
	var delete []int
	var previousComments []int
	var latestComment int
	var entries []string
	// First accumulate result entries and comment IDs
	for _, ic := range ics {
		if ic.User.Login != botName {
			continue
		}
		// Old report comments started with the context. Delete them.
		// TODO(spxtr): Delete this check a few weeks after this merges.
		if strings.HasPrefix(ic.Body, pj.Spec.Context) {
			delete = append(delete, ic.ID)
		}
		if !strings.Contains(ic.Body, commentTag) {
			continue
		}
		if latestComment != 0 {
			previousComments = append(previousComments, latestComment)
		}
		latestComment = ic.ID
		var tracking bool
		for _, line := range strings.Split(ic.Body, "\n") {
			line = strings.TrimSpace(line)
			if strings.HasPrefix(line, "---") {
				tracking = true
			} else if len(line) == 0 {
				tracking = false
			} else if tracking {
				entries = append(entries, line)
			}
		}
	}
	var newEntries []string
	// Next decide which entries to keep.
	for i := range entries {
		keep := true
		f1 := strings.Split(entries[i], " | ")
		for j := range entries {
			if i == j {
				continue
			}
			f2 := strings.Split(entries[j], " | ")
			// Use the newer results if there are multiple.
			if j > i && f2[0] == f1[0] {
				keep = false
			}
		}
		// Use the current result if there is an old one.
		if pj.Spec.Context == f1[0] {
			keep = false
		}
		if keep {
			newEntries = append(newEntries, entries[i])
		}
	}
	var createNewComment bool
	if string(pj.Status.State) == github.StatusFailure {
		newEntries = append(newEntries, createEntry(pj))
		createNewComment = true
	}
	delete = append(delete, previousComments...)
	if (createNewComment || len(newEntries) == 0) && latestComment != 0 {
		delete = append(delete, latestComment)
		latestComment = 0
	}
	return delete, newEntries, latestComment
}

// parseIssueComments returns a list of comments to delete, a list of table
// entries, and the ID of the comment to update. If there are no table entries
// then don't make a new comment. Otherwise, if the comment to update is 0,
// create a new comment.

parseIssueComments returns a list of comments to delete a list of table entries and the ID of the comment to update . If there are no table entries then don t make a new comment . Otherwise if the comment to update is 0 create a new comment .


func createComment(reportTemplate *template.Template, pj prowapi.ProwJob, entries []string) (string, error) {
	plural := ""
	if len(entries) > 1 {
		plural = "s"
	}
	var b bytes.Buffer
	if reportTemplate != nil {
		if err := reportTemplate.Execute(&b, &pj); err != nil {
			return "", err
		}
	}
	lines := []string{
		fmt.Sprintf("@%s: The following test%s **failed**, say `/retest` to rerun them all:", pj.Spec.Refs.Pulls[0].Author, plural),
		"",
		"Test name | Commit | Details | Rerun command",
		"--- | --- | --- | ---",
	}
	lines = append(lines, entries...)
	if reportTemplate != nil {
		lines = append(lines, "", b.String())
	}
	lines = append(lines, []string{
		"",
		"<details>",
		"",
		plugins.AboutThisBot,
		"</details>",
		commentTag,
	}...)
	return strings.Join(lines, "\n"), nil
}

func createComment ( reportTemplate * template . Template , pj prowapi . ProwJob , entries [ ] string ) ( string , error ) { plural := " " if len ( entries ) > 1 { plural = " " } var b bytes . Buffer if reportTemplate != nil { if err := reportTemplate . Execute ( & b , & pj ) ; err != nil { return " " , err } } lines := [ ] string { fmt . Sprintf ( " " , pj . Spec . Refs . Pulls [ 0 ] . Author , plural ) , " " , " " , " " , } lines = append ( lines , entries ... ) if reportTemplate != nil { lines = append ( lines , " " , b . String ( ) ) } lines = append ( lines , [ ] string { " " , " " , " " , plugins . AboutThisBot , " " , commentTag , } ... ) return strings . Join ( lines , " \n " ) , nil }

// createComment take a ProwJob and a list of entries generated with
// createEntry and returns a nicely formatted comment. It may fail if template
// execution fails.

createComment take a ProwJob and a list of entries generated with createEntry and returns a nicely formatted comment . It may fail if template execution fails .


func (lens Lens) Config() lenses.LensConfig {
	return lenses.LensConfig{
		Name:     name,
		Title:    title,
		Priority: priority,
	}
}

func ( lens Lens ) Config ( ) lenses . LensConfig { return lenses . LensConfig { Name : name , Title : title , Priority : priority , } }

// Config returns the lens's configuration.

Config returns the lens s configuration .


func (lens Lens) Callback(artifacts []lenses.Artifact, resourceDir string, data string) string {
	return ""
}

func ( lens Lens ) Callback ( artifacts [ ] lenses . Artifact , resourceDir string , data string ) string { return " " }


func FormatRecord(record Record) string {
	output := bytes.Buffer{}
	if record.Failed {
		fmt.Fprintln(&output, "# FAILED!")
	}
	fmt.Fprintf(&output, "# Cloning %s/%s at %s", record.Refs.Org, record.Refs.Repo, record.Refs.BaseRef)
	if record.Refs.BaseSHA != "" {
		fmt.Fprintf(&output, "(%s)", record.Refs.BaseSHA)
	}
	output.WriteString("\n")
	if len(record.Refs.Pulls) > 0 {
		output.WriteString("# Checking out pulls:\n")
		for _, pull := range record.Refs.Pulls {
			fmt.Fprintf(&output, "#\t%d", pull.Number)
			if pull.SHA != "" {
				fmt.Fprintf(&output, "(%s)", pull.SHA)
			}
			fmt.Fprint(&output, "\n")
		}
	}
	for _, command := range record.Commands {
		fmt.Fprintf(&output, "$ %s\n", command.Command)
		fmt.Fprint(&output, command.Output)
		if command.Error != "" {
			fmt.Fprintf(&output, "# Error: %s\n", command.Error)
		}
	}

	return output.String()
}

func FormatRecord ( record Record ) string { output := bytes . Buffer { } if record . Failed { fmt . Fprintln ( & output , " " ) } fmt . Fprintf ( & output , " " , record . Refs . Org , record . Refs . Repo , record . Refs . BaseRef ) if record . Refs . BaseSHA != " " { fmt . Fprintf ( & output , " " , record . Refs . BaseSHA ) } output . WriteString ( " \n " ) if len ( record . Refs . Pulls ) > 0 { output . WriteString ( " \n " ) for _ , pull := range record . Refs . Pulls { fmt . Fprintf ( & output , " \t " , pull . Number ) if pull . SHA != " " { fmt . Fprintf ( & output , " " , pull . SHA ) } fmt . Fprint ( & output , " \n " ) } } for _ , command := range record . Commands { fmt . Fprintf ( & output , " \n " , command . Command ) fmt . Fprint ( & output , command . Output ) if command . Error != " " { fmt . Fprintf ( & output , " \n " , command . Error ) } } return output . String ( ) }

// FormatRecord describes the record in a human-readable
// manner for inclusion into build logs

FormatRecord describes the record in a human - readable manner for inclusion into build logs


func (c *Client) Namespace(ns string) *Client {
	nc := *c
	nc.namespace = ns
	return &nc
}

func ( c * Client ) Namespace ( ns string ) * Client { nc := * c nc . namespace = ns return & nc }

// Namespace returns a copy of the client pointing at the specified namespace.

Namespace returns a copy of the client pointing at the specified namespace .


func NewFakeClient(deckURL string) *Client {
	return &Client{
		namespace: "default",
		deckURL:   deckURL,
		client:    &http.Client{},
		fake:      true,
	}
}

func NewFakeClient ( deckURL string ) * Client { return & Client { namespace : " " , deckURL : deckURL , client : & http . Client { } , fake : true , } }

// NewFakeClient creates a client that doesn't do anything. If you provide a
// deck URL then the client will hit that for the supported calls.

NewFakeClient creates a client that doesn t do anything . If you provide a deck URL then the client will hit that for the supported calls .


func NewClientInCluster(namespace string) (*Client, error) {
	tokenFile := "/var/run/secrets/kubernetes.io/serviceaccount/token"
	token, err := ioutil.ReadFile(tokenFile)
	if err != nil {
		return nil, err
	}

	rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
	certData, err := ioutil.ReadFile(rootCAFile)
	if err != nil {
		return nil, err
	}

	cp := x509.NewCertPool()
	cp.AppendCertsFromPEM(certData)

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{
			MinVersion: tls.VersionTLS12,
			RootCAs:    cp,
		},
	}
	return &Client{
		logger:    logrus.WithField("client", "kube"),
		baseURL:   inClusterBaseURL,
		client:    &http.Client{Transport: tr, Timeout: requestTimeout},
		token:     string(token),
		namespace: namespace,
	}, nil
}

func NewClientInCluster ( namespace string ) ( * Client , error ) { tokenFile := " " token , err := ioutil . ReadFile ( tokenFile ) if err != nil { return nil , err } rootCAFile := " " certData , err := ioutil . ReadFile ( rootCAFile ) if err != nil { return nil , err } cp := x509 . NewCertPool ( ) cp . AppendCertsFromPEM ( certData ) tr := & http . Transport { TLSClientConfig : & tls . Config { MinVersion : tls . VersionTLS12 , RootCAs : cp , } , } return & Client { logger : logrus . WithField ( " " , " " ) , baseURL : inClusterBaseURL , client : & http . Client { Transport : tr , Timeout : requestTimeout } , token : string ( token ) , namespace : namespace , } , nil }

// NewClientInCluster creates a Client that works from within a pod.

NewClientInCluster creates a Client that works from within a pod .


func NewClientFromFile(clusterPath, namespace string) (*Client, error) {
	data, err := ioutil.ReadFile(clusterPath)
	if err != nil {
		return nil, err
	}
	var c Cluster
	if err := yaml.Unmarshal(data, &c); err != nil {
		return nil, err
	}
	return NewClient(&c, namespace)
}

func NewClientFromFile ( clusterPath , namespace string ) ( * Client , error ) { data , err := ioutil . ReadFile ( clusterPath ) if err != nil { return nil , err } var c Cluster if err := yaml . Unmarshal ( data , & c ) ; err != nil { return nil , err } return NewClient ( & c , namespace ) }

// NewClientFromFile reads a Cluster object at clusterPath and returns an
// authenticated client using the keys within.

NewClientFromFile reads a Cluster object at clusterPath and returns an authenticated client using the keys within .


func NewClient(c *Cluster, namespace string) (*Client, error) {
	// Relies on json encoding/decoding []byte as base64
	// https://golang.org/pkg/encoding/json/#Marshal
	cc := c.ClientCertificate
	ck := c.ClientKey
	ca := c.ClusterCACertificate

	cert, err := tls.X509KeyPair(cc, ck)
	if err != nil {
		return nil, err
	}

	cp := x509.NewCertPool()
	cp.AppendCertsFromPEM(ca)

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{
			MinVersion:   tls.VersionTLS12,
			Certificates: []tls.Certificate{cert},
			RootCAs:      cp,
		},
	}
	return &Client{
		logger:    logrus.WithField("client", "kube"),
		baseURL:   c.Endpoint,
		client:    &http.Client{Transport: tr, Timeout: requestTimeout},
		namespace: namespace,
	}, nil
}

// NewClient returns an authenticated Client using the keys in the Cluster.

NewClient returns an authenticated Client using the keys in the Cluster .


func (c *Client) ReplaceConfigMap(name string, config ConfigMap) (ConfigMap, error) {
	c.log("ReplaceConfigMap", name)
	namespace := c.namespace
	if config.Namespace != "" {
		namespace = config.Namespace
	}
	var retConfigMap ConfigMap
	err := c.request(&request{
		method:      http.MethodPut,
		path:        fmt.Sprintf("/api/v1/namespaces/%s/configmaps/%s", namespace, name),
		requestBody: &config,
	}, &retConfigMap)

	return retConfigMap, err
}

func ( c * Client ) ReplaceConfigMap ( name string , config ConfigMap ) ( ConfigMap , error ) { c . log ( " " , name ) namespace := c . namespace if config . Namespace != " " { namespace = config . Namespace } var retConfigMap ConfigMap err := c . request ( & request { method : http . MethodPut , path : fmt . Sprintf ( " " , namespace , name ) , requestBody : & config , } , & retConfigMap ) return retConfigMap , err }

// ReplaceConfigMap puts the configmap into name.
//
// Analogous to kubectl replace configmap
//
// If config.Namespace is empty, the client's specified namespace is used.
// Returns the content returned by the apiserver

ReplaceConfigMap puts the configmap into name . Analogous to kubectl replace configmap If config . Namespace is empty the client s specified namespace is used . Returns the content returned by the apiserver


func GetDiskUsage(path string) (percentBlocksFree float64, bytesFree, bytesUsed uint64, err error) {
	var stat syscall.Statfs_t
	err = syscall.Statfs(path, &stat)
	if err != nil {
		return 0, 0, 0, err
	}
	percentBlocksFree = float64(stat.Bfree) / float64(stat.Blocks) * 100
	bytesFree = stat.Bfree * uint64(stat.Bsize)
	bytesUsed = (stat.Blocks - stat.Bfree) * uint64(stat.Bsize)
	return percentBlocksFree, bytesFree, bytesUsed, nil
}

func GetDiskUsage ( path string ) ( percentBlocksFree float64 , bytesFree , bytesUsed uint64 , err error ) { var stat syscall . Statfs_t err = syscall . Statfs ( path , & stat ) if err != nil { return 0 , 0 , 0 , err } percentBlocksFree = float64 ( stat . Bfree ) / float64 ( stat . Blocks ) * 100 bytesFree = stat . Bfree * uint64 ( stat . Bsize ) bytesUsed = ( stat . Blocks - stat . Bfree ) * uint64 ( stat . Bsize ) return percentBlocksFree , bytesFree , bytesUsed , nil }

// GetDiskUsage wraps syscall.Statfs for usage in GCing the disk

GetDiskUsage wraps syscall . Statfs for usage in GCing the disk


func GetATime(path string, defaultTime time.Time) time.Time {
	at, err := atime.Stat(path)
	if err != nil {
		log.WithError(err).Errorf("Could not get atime for %s", path)
		return defaultTime
	}
	return at
}

func GetATime ( path string , defaultTime time . Time ) time . Time { at , err := atime . Stat ( path ) if err != nil { log . WithError ( err ) . Errorf ( " " , path ) return defaultTime } return at }

// GetATime the atime for a file, logging errors instead of failing
// and returning defaultTime instead

GetATime the atime for a file logging errors instead of failing and returning defaultTime instead


func RegisterLens(lens Lens) error {
	config := lens.Config()
	_, ok := lensReg[config.Name]
	if ok {
		return fmt.Errorf("viewer already registered with name %s", config.Name)
	}

	if config.Title == "" {
		return errors.New("empty title field in view metadata")
	}
	if config.Priority < 0 {
		return errors.New("priority must be >=0")
	}
	lensReg[config.Name] = lens
	logrus.Infof("Spyglass registered viewer %s with title %s.", config.Name, config.Title)
	return nil
}

func RegisterLens ( lens Lens ) error { config := lens . Config ( ) _ , ok := lensReg [ config . Name ] if ok { return fmt . Errorf ( " " , config . Name ) } if config . Title == " " { return errors . New ( " " ) } if config . Priority < 0 { return errors . New ( " " ) } lensReg [ config . Name ] = lens logrus . Infof ( " " , config . Name , config . Title ) return nil }


func GetLens(name string) (Lens, error) {
	lens, ok := lensReg[name]
	if !ok {
		return nil, ErrInvalidLensName
	}
	return lens, nil
}

func GetLens ( name string ) ( Lens , error ) { lens , ok := lensReg [ name ] if ! ok { return nil , ErrInvalidLensName } return lens , nil }

// GetLens returns a Lens by name, if it exists; otherwise it returns an error.

GetLens returns a Lens by name if it exists ; otherwise it returns an error .


func LastNLines(a Artifact, n int64) ([]string, error) {
	// 300B, a reasonable log line length, probably a bit more scalable than a hard-coded value
	return LastNLinesChunked(a, n, 300*n+1)
}

// LastNLines reads the last n lines from an artifact.

LastNLines reads the last n lines from an artifact .


func NewClient(tokenGenerator func() []byte) *Client {
	return &Client{
		logger:         logrus.WithField("client", "slack"),
		tokenGenerator: tokenGenerator,
	}
}

func NewClient ( tokenGenerator func ( ) [ ] byte ) * Client { return & Client { logger : logrus . WithField ( " " , " " ) , tokenGenerator : tokenGenerator , } }

// NewClient creates a slack client with an API token.

NewClient creates a slack client with an API token .


func (sl *Client) WriteMessage(text, channel string) error {
	sl.log("WriteMessage", text, channel)
	if sl.fake {
		return nil
	}

	var uv = sl.urlValues()
	uv.Add("channel", channel)
	uv.Add("text", text)

	_, err := sl.postMessage(chatPostMessage, uv)
	return err
}

func ( sl * Client ) WriteMessage ( text , channel string ) error { sl . log ( " " , text , channel ) if sl . fake { return nil } var uv = sl . urlValues ( ) uv . Add ( " " , channel ) uv . Add ( " " , text ) _ , err := sl . postMessage ( chatPostMessage , uv ) return err }


func (NATGateway) MarkAndSweep(sess *session.Session, acct string, region string, set *Set) error {
	svc := ec2.New(sess, &aws.Config{Region: aws.String(region)})

	inp := &ec2.DescribeNatGatewaysInput{}
	if err := svc.DescribeNatGatewaysPages(inp, func(page *ec2.DescribeNatGatewaysOutput, _ bool) bool {
		for _, gw := range page.NatGateways {
			g := &natGateway{
				Account: acct,
				Region:  region,
				ID:      *gw.NatGatewayId,
			}

			if set.Mark(g) {
				inp := &ec2.DeleteNatGatewayInput{NatGatewayId: gw.NatGatewayId}
				if _, err := svc.DeleteNatGateway(inp); err != nil {
					klog.Warningf("%v: delete failed: %v", g.ARN(), err)
				}
			}
		}
		return true
	}); err != nil {
		return err
	}

	return nil
}

func ( NATGateway ) MarkAndSweep ( sess * session . Session , acct string , region string , set * Set ) error { svc := ec2 . New ( sess , & aws . Config { Region : aws . String ( region ) } ) inp := & ec2 . DescribeNatGatewaysInput { } if err := svc . DescribeNatGatewaysPages ( inp , func ( page * ec2 . DescribeNatGatewaysOutput , _ bool ) bool { for _ , gw := range page . NatGateways { g := & natGateway { Account : acct , Region : region , ID : * gw . NatGatewayId , } if set . Mark ( g ) { inp := & ec2 . DeleteNatGatewayInput { NatGatewayId : gw . NatGatewayId } if _ , err := svc . DeleteNatGateway ( inp ) ; err != nil { klog . Warningf ( " " , g . ARN ( ) , err ) } } } return true } ) ; err != nil { return err } return nil }

// MarkAndSweep looks at the provided set, and removes resources older than its TTL that have been previously tagged.

MarkAndSweep looks at the provided set and removes resources older than its TTL that have been previously tagged .


func (NATGateway) ListAll(sess *session.Session, acct, region string) (*Set, error) {
	svc := ec2.New(sess, &aws.Config{Region: aws.String(region)})
	set := NewSet(0)
	inp := &ec2.DescribeNatGatewaysInput{}

	err := svc.DescribeNatGatewaysPages(inp, func(page *ec2.DescribeNatGatewaysOutput, _ bool) bool {
		for _, gw := range page.NatGateways {
			now := time.Now()
			arn := natGateway{
				Account: acct,
				Region:  region,
				ID:      *gw.NatGatewayId,
			}.ARN()

			set.firstSeen[arn] = now
		}

		return true
	})

	return set, errors.Wrapf(err, "couldn't describe nat gateways for %q in %q", acct, region)
}

func ( NATGateway ) ListAll ( sess * session . Session , acct , region string ) ( * Set , error ) { svc := ec2 . New ( sess , & aws . Config { Region : aws . String ( region ) } ) set := NewSet ( 0 ) inp := & ec2 . DescribeNatGatewaysInput { } err := svc . DescribeNatGatewaysPages ( inp , func ( page * ec2 . DescribeNatGatewaysOutput , _ bool ) bool { for _ , gw := range page . NatGateways { now := time . Now ( ) arn := natGateway { Account : acct , Region : region , ID : * gw . NatGatewayId , } . ARN ( ) set . firstSeen [ arn ] = now } return true } ) return set , errors . Wrapf ( err , " " , acct , region ) }

// ListAll populates a set will all available NATGateway resources.

ListAll populates a set will all available NATGateway resources .


func NewClient(owner string, url string) *Client {

	client := &Client{
		url:     url,
		owner:   owner,
		storage: storage.NewMemoryStorage(),
	}

	// Configure the dialer to attempt three additional times to establish
	// a connection after a failed dial attempt. The dialer should wait 10
	// seconds between each attempt.
	client.Dialer.RetryCount = 3
	client.Dialer.RetrySleep = time.Second * 10

	// Configure the dialer and HTTP client transport to mimic the configuration
	// of the http.DefaultTransport with the exception that the Dialer's Dial
	// and DialContext functions are assigned to the client transport.
	//
	// See https://golang.org/pkg/net/http/#RoundTripper for the values
	// values used for the http.DefaultTransport.
	client.Dialer.Timeout = 30 * time.Second
	client.Dialer.KeepAlive = 30 * time.Second
	client.Dialer.DualStack = true
	client.http.Transport = &http.Transport{
		Proxy:                 http.ProxyFromEnvironment,
		Dial:                  client.Dialer.Dial,
		DialContext:           client.Dialer.DialContext,
		MaxIdleConns:          100,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}

	return client
}

// NewClient creates a Boskos client for the specified URL and resource owner.
//
// Clients created with this function default to retrying failed connection
// attempts three times with a ten second pause between each attempt.

NewClient creates a Boskos client for the specified URL and resource owner . Clients created with this function default to retrying failed connection attempts three times with a ten second pause between each attempt .


func (c *Client) Acquire(rtype, state, dest string) (*common.Resource, error) {
	r, err := c.acquire(rtype, state, dest)
	if err != nil {
		return nil, err
	}
	c.lock.Lock()
	defer c.lock.Unlock()
	if r != nil {
		c.storage.Add(*r)
	}

	return r, nil
}

func ( c * Client ) Acquire ( rtype , state , dest string ) ( * common . Resource , error ) { r , err := c . acquire ( rtype , state , dest ) if err != nil { return nil , err } c . lock . Lock ( ) defer c . lock . Unlock ( ) if r != nil { c . storage . Add ( * r ) } return r , nil }

// public method
// Acquire asks boskos for a resource of certain type in certain state, and set the resource to dest state.
// Returns the resource on success.

public method Acquire asks boskos for a resource of certain type in certain state and set the resource to dest state . Returns the resource on success .


func (c *Client) AcquireWait(ctx context.Context, rtype, state, dest string) (*common.Resource, error) {
	if ctx == nil {
		return nil, ErrContextRequired
	}
	// Try to acquire the resource until available or the context is
	// cancelled or its deadline exceeded.
	for {
		r, err := c.Acquire(rtype, state, dest)
		if err != nil {
			if err == ErrAlreadyInUse || err == ErrNotFound {
				select {
				case <-ctx.Done():
					return nil, err
				case <-time.After(3 * time.Second):
					continue
				}
			}
			return nil, err
		}
		return r, nil
	}
}

// AcquireWait blocks until Acquire returns the specified resource or the
// provided context is cancelled or its deadline exceeded.

AcquireWait blocks until Acquire returns the specified resource or the provided context is cancelled or its deadline exceeded .


func (c *Client) AcquireByState(state, dest string, names []string) ([]common.Resource, error) {
	resources, err := c.acquireByState(state, dest, names)
	if err != nil {
		return nil, err
	}
	c.lock.Lock()
	defer c.lock.Unlock()
	for _, r := range resources {
		c.storage.Add(r)
	}
	return resources, nil
}

func ( c * Client ) AcquireByState ( state , dest string , names [ ] string ) ( [ ] common . Resource , error ) { resources , err := c . acquireByState ( state , dest , names ) if err != nil { return nil , err } c . lock . Lock ( ) defer c . lock . Unlock ( ) for _ , r := range resources { c . storage . Add ( r ) } return resources , nil }

// AcquireByState asks boskos for a resources of certain type, and set the resource to dest state.
// Returns a list of resources on success.

AcquireByState asks boskos for a resources of certain type and set the resource to dest state . Returns a list of resources on success .


func (c *Client) ReleaseAll(dest string) error {
	c.lock.Lock()
	defer c.lock.Unlock()
	resources, err := c.storage.List()
	if err != nil {
		return err
	}
	if len(resources) == 0 {
		return fmt.Errorf("no holding resource")
	}
	var allErrors error
	for _, r := range resources {
		c.storage.Delete(r.GetName())
		err := c.release(r.GetName(), dest)
		if err != nil {
			allErrors = multierror.Append(allErrors, err)
		}
	}
	return allErrors
}

func ( c * Client ) ReleaseAll ( dest string ) error { c . lock . Lock ( ) defer c . lock . Unlock ( ) resources , err := c . storage . List ( ) if err != nil { return err } if len ( resources ) == 0 { return fmt . Errorf ( " " ) } var allErrors error for _ , r := range resources { c . storage . Delete ( r . GetName ( ) ) err := c . release ( r . GetName ( ) , dest ) if err != nil { allErrors = multierror . Append ( allErrors , err ) } } return allErrors }

// ReleaseAll returns all resources hold by the client back to boskos and set them to dest state.

ReleaseAll returns all resources hold by the client back to boskos and set them to dest state .


func (c *Client) ReleaseOne(name, dest string) error {
	c.lock.Lock()
	defer c.lock.Unlock()

	if _, err := c.storage.Get(name); err != nil {
		return fmt.Errorf("no resource name %v", name)
	}
	c.storage.Delete(name)
	if err := c.release(name, dest); err != nil {
		return err
	}
	return nil
}

func ( c * Client ) ReleaseOne ( name , dest string ) error { c . lock . Lock ( ) defer c . lock . Unlock ( ) if _ , err := c . storage . Get ( name ) ; err != nil { return fmt . Errorf ( " " , name ) } c . storage . Delete ( name ) if err := c . release ( name , dest ) ; err != nil { return err } return nil }

// ReleaseOne returns one of owned resources back to boskos and set it to dest state.

ReleaseOne returns one of owned resources back to boskos and set it to dest state .


func (c *Client) UpdateAll(state string) error {
	c.lock.Lock()
	defer c.lock.Unlock()

	resources, err := c.storage.List()
	if err != nil {
		return err
	}
	if len(resources) == 0 {
		return fmt.Errorf("no holding resource")
	}
	var allErrors error
	for _, r := range resources {
		if err := c.update(r.GetName(), state, nil); err != nil {
			allErrors = multierror.Append(allErrors, err)
			continue
		}
		if err := c.updateLocalResource(r, state, nil); err != nil {
			allErrors = multierror.Append(allErrors, err)
		}
	}
	return allErrors
}

func ( c * Client ) UpdateAll ( state string ) error { c . lock . Lock ( ) defer c . lock . Unlock ( ) resources , err := c . storage . List ( ) if err != nil { return err } if len ( resources ) == 0 { return fmt . Errorf ( " " ) } var allErrors error for _ , r := range resources { if err := c . update ( r . GetName ( ) , state , nil ) ; err != nil { allErrors = multierror . Append ( allErrors , err ) continue } if err := c . updateLocalResource ( r , state , nil ) ; err != nil { allErrors = multierror . Append ( allErrors , err ) } } return allErrors }

// UpdateAll signals update for all resources hold by the client.

UpdateAll signals update for all resources hold by the client .


func (c *Client) SyncAll() error {
	c.lock.Lock()
	defer c.lock.Unlock()

	resources, err := c.storage.List()
	if err != nil {
		return err
	}
	if len(resources) == 0 {
		logrus.Info("no resource to sync")
		return nil
	}
	var allErrors error
	for _, i := range resources {
		r, err := common.ItemToResource(i)
		if err != nil {
			allErrors = multierror.Append(allErrors, err)
			continue
		}
		if err := c.update(r.Name, r.State, nil); err != nil {
			allErrors = multierror.Append(allErrors, err)
			continue
		}
		if err := c.storage.Update(r); err != nil {
			allErrors = multierror.Append(allErrors, err)
		}
	}
	return allErrors
}

func ( c * Client ) SyncAll ( ) error { c . lock . Lock ( ) defer c . lock . Unlock ( ) resources , err := c . storage . List ( ) if err != nil { return err } if len ( resources ) == 0 { logrus . Info ( " " ) return nil } var allErrors error for _ , i := range resources { r , err := common . ItemToResource ( i ) if err != nil { allErrors = multierror . Append ( allErrors , err ) continue } if err := c . update ( r . Name , r . State , nil ) ; err != nil { allErrors = multierror . Append ( allErrors , err ) continue } if err := c . storage . Update ( r ) ; err != nil { allErrors = multierror . Append ( allErrors , err ) } } return allErrors }

// SyncAll signals update for all resources hold by the client.

SyncAll signals update for all resources hold by the client .


func (c *Client) UpdateOne(name, state string, userData *common.UserData) error {
	c.lock.Lock()
	defer c.lock.Unlock()

	r, err := c.storage.Get(name)
	if err != nil {
		return fmt.Errorf("no resource name %v", name)
	}
	if err := c.update(r.GetName(), state, userData); err != nil {
		return err
	}
	return c.updateLocalResource(r, state, userData)
}

func ( c * Client ) UpdateOne ( name , state string , userData * common . UserData ) error { c . lock . Lock ( ) defer c . lock . Unlock ( ) r , err := c . storage . Get ( name ) if err != nil { return fmt . Errorf ( " " , name ) } if err := c . update ( r . GetName ( ) , state , userData ) ; err != nil { return err } return c . updateLocalResource ( r , state , userData ) }

// UpdateOne signals update for one of the resources hold by the client.

UpdateOne signals update for one of the resources hold by the client .


func (c *Client) Metric(rtype string) (common.Metric, error) {
	return c.metric(rtype)
}

func ( c * Client ) Metric ( rtype string ) ( common . Metric , error ) { return c . metric ( rtype ) }

// Metric will query current metric for target resource type.
// Return a common.Metric object on success.

Metric will query current metric for target resource type . Return a common . Metric object on success .


func (c *Client) HasResource() bool {
	resources, _ := c.storage.List()
	return len(resources) > 0
}

func ( c * Client ) HasResource ( ) bool { resources , _ := c . storage . List ( ) return len ( resources ) > 0 }

// HasResource tells if current client holds any resources


func (d *DialerWithRetry) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
	// Always bump the retry count by 1 in order to equal the actual number of
	// attempts. For example, if a retry count of 2 is specified, the intent
	// is for three attempts -- the initial attempt with two retries in case
	// the initial attempt times out.
	count := d.RetryCount + 1
	sleep := d.RetrySleep
	i := uint(0)
	for {
		conn, err := d.Dialer.DialContext(ctx, network, address)
		if err != nil {
			if isDialErrorRetriable(err) {
				if i < count-1 {
					select {
					case <-time.After(sleep):
						i++
						continue
					case <-ctx.Done():
						return nil, err
					}
				}
			}
			return nil, err
		}
		return conn, nil
	}
}

// DialContext connects to the address on the named network using the provided context.

DialContext connects to the address on the named network using the provided context .


func NewDashboardAgent(repos []string, config *config.GitHubOAuthConfig, log *logrus.Entry) *DashboardAgent {
	return &DashboardAgent{
		repos: repos,
		goac:  config,
		log:   log,
	}
}

func NewDashboardAgent ( repos [ ] string , config * config . GitHubOAuthConfig , log * logrus . Entry ) * DashboardAgent { return & DashboardAgent { repos : repos , goac : config , log : log , } }

// NewDashboardAgent creates a new user dashboard agent .