func (r *Repo) Merge(commitlike string) (bool, error) {
	r.logger.Infof("Merging %s.", commitlike)
	co := r.gitCommand("merge", "--no-ff", "--no-stat", "-m merge", commitlike)

	b, err := co.CombinedOutput()
	if err == nil {
		return true, nil
	}
	r.logger.WithError(err).Infof("Merge failed with output: %s", string(b))

	if b, err := r.gitCommand("merge", "--abort").CombinedOutput(); err != nil {
		return false, fmt.Errorf("error aborting merge for commitlike %s: %v. output: %s", commitlike, err, string(b))
	}

	return false, nil
}

func ( r * Repo ) Merge ( commitlike string ) ( bool , error ) { r . logger . Infof ( " " , commitlike ) co := r . gitCommand ( " " , " " , " " , " " , commitlike ) b , err := co . CombinedOutput ( ) if err == nil { return true , nil } r . logger . WithError ( err ) . Infof ( " " , string ( b ) ) if b , err := r . gitCommand ( " " , " " ) . CombinedOutput ( ) ; err != nil { return false , fmt . Errorf ( " " , commitlike , err , string ( b ) ) } return false , nil }

// Merge attempts to merge commitlike into the current branch. It returns true
// if the merge completes. It returns an error if the abort fails.

Merge attempts to merge commitlike into the current branch . It returns true if the merge completes . It returns an error if the abort fails .


func (r *Repo) CheckoutPullRequest(number int) error {
	r.logger.Infof("Fetching and checking out %s#%d.", r.repo, number)
	if b, err := retryCmd(r.logger, r.Dir, r.git, "fetch", r.base+"/"+r.repo, fmt.Sprintf("pull/%d/head:pull%d", number, number)); err != nil {
		return fmt.Errorf("git fetch failed for PR %d: %v. output: %s", number, err, string(b))
	}
	co := r.gitCommand("checkout", fmt.Sprintf("pull%d", number))
	if b, err := co.CombinedOutput(); err != nil {
		return fmt.Errorf("git checkout failed for PR %d: %v. output: %s", number, err, string(b))
	}
	return nil
}

func ( r * Repo ) CheckoutPullRequest ( number int ) error { r . logger . Infof ( " " , r . repo , number ) if b , err := retryCmd ( r . logger , r . Dir , r . git , " " , r . base + " " + r . repo , fmt . Sprintf ( " " , number , number ) ) ; err != nil { return fmt . Errorf ( " " , number , err , string ( b ) ) } co := r . gitCommand ( " " , fmt . Sprintf ( " " , number ) ) if b , err := co . CombinedOutput ( ) ; err != nil { return fmt . Errorf ( " " , number , err , string ( b ) ) } return nil }

// CheckoutPullRequest does exactly that.


func (r *Repo) Config(key, value string) error {
	r.logger.Infof("Running git config %s %s", key, value)
	if b, err := r.gitCommand("config", key, value).CombinedOutput(); err != nil {
		return fmt.Errorf("git config %s %s failed: %v. output: %s", key, value, err, string(b))
	}
	return nil
}

func ( r * Repo ) Config ( key , value string ) error { r . logger . Infof ( " " , key , value ) if b , err := r . gitCommand ( " " , key , value ) . CombinedOutput ( ) ; err != nil { return fmt . Errorf ( " " , key , value , err , string ( b ) ) } return nil }


func retryCmd(l *logrus.Entry, dir, cmd string, arg ...string) ([]byte, error) {
	var b []byte
	var err error
	sleepyTime := time.Second
	for i := 0; i < 3; i++ {
		c := exec.Command(cmd, arg...)
		c.Dir = dir
		b, err = c.CombinedOutput()
		if err != nil {
			l.Warningf("Running %s %v returned error %v with output %s.", cmd, arg, err, string(b))
			time.Sleep(sleepyTime)
			sleepyTime *= 2
			continue
		}
		break
	}
	return b, err
}

func retryCmd ( l * logrus . Entry , dir , cmd string , arg ... string ) ( [ ] byte , error ) { var b [ ] byte var err error sleepyTime := time . Second for i := 0 ; i < 3 ; i ++ { c := exec . Command ( cmd , arg ... ) c . Dir = dir b , err = c . CombinedOutput ( ) if err != nil { l . Warningf ( " " , cmd , arg , err , string ( b ) ) time . Sleep ( sleepyTime ) sleepyTime *= 2 continue } break } return b , err }

// retryCmd will retry the command a few times with backoff. Use this for any
// commands that will be talking to GitHub, such as clones or fetches.

retryCmd will retry the command a few times with backoff . Use this for any commands that will be talking to GitHub such as clones or fetches .


func LabelsAndAnnotationsForSpec(spec prowapi.ProwJobSpec, extraLabels, extraAnnotations map[string]string) (map[string]string, map[string]string) {
	jobNameForLabel := spec.Job
	if len(jobNameForLabel) > validation.LabelValueMaxLength {
		// TODO(fejta): consider truncating middle rather than end.
		jobNameForLabel = strings.TrimRight(spec.Job[:validation.LabelValueMaxLength], ".-")
		logrus.WithFields(logrus.Fields{
			"job":       spec.Job,
			"key":       kube.ProwJobAnnotation,
			"value":     spec.Job,
			"truncated": jobNameForLabel,
		}).Info("Cannot use full job name, will truncate.")
	}
	labels := map[string]string{
		kube.CreatedByProw:     "true",
		kube.ProwJobTypeLabel:  string(spec.Type),
		kube.ProwJobAnnotation: jobNameForLabel,
	}
	if spec.Type != prowapi.PeriodicJob && spec.Refs != nil {
		labels[kube.OrgLabel] = spec.Refs.Org
		labels[kube.RepoLabel] = spec.Refs.Repo
		if len(spec.Refs.Pulls) > 0 {
			labels[kube.PullLabel] = strconv.Itoa(spec.Refs.Pulls[0].Number)
		}
	}

	for k, v := range extraLabels {
		labels[k] = v
	}

	// let's validate labels
	for key, value := range labels {
		if errs := validation.IsValidLabelValue(value); len(errs) > 0 {
			// try to use basename of a path, if path contains invalid //
			base := filepath.Base(value)
			if errs := validation.IsValidLabelValue(base); len(errs) == 0 {
				labels[key] = base
				continue
			}
			logrus.WithFields(logrus.Fields{
				"key":    key,
				"value":  value,
				"errors": errs,
			}).Warn("Removing invalid label")
			delete(labels, key)
		}
	}

	annotations := map[string]string{
		kube.ProwJobAnnotation: spec.Job,
	}
	for k, v := range extraAnnotations {
		annotations[k] = v
	}

	return labels, annotations
}

// LabelsAndAnnotationsForSpec returns a minimal set of labels to add to prowjobs or its owned resources.
//
// User-provided extraLabels and extraAnnotations values will take precedence over auto-provided values.

LabelsAndAnnotationsForSpec returns a minimal set of labels to add to prowjobs or its owned resources . User - provided extraLabels and extraAnnotations values will take precedence over auto - provided values .


func ProwJobToPod(pj prowapi.ProwJob, buildID string) (*coreapi.Pod, error) {
	if pj.Spec.PodSpec == nil {
		return nil, fmt.Errorf("prowjob %q lacks a pod spec", pj.Name)
	}

	rawEnv, err := downwardapi.EnvForSpec(downwardapi.NewJobSpec(pj.Spec, buildID, pj.Name))
	if err != nil {
		return nil, err
	}

	spec := pj.Spec.PodSpec.DeepCopy()
	spec.RestartPolicy = "Never"
	spec.Containers[0].Name = kube.TestContainerName

	// if the user has not provided a serviceaccount to use or explicitly
	// requested mounting the default token, we treat the unset value as
	// false, while kubernetes treats it as true if it is unset because
	// it was added in v1.6
	if spec.AutomountServiceAccountToken == nil && spec.ServiceAccountName == "" {
		myFalse := false
		spec.AutomountServiceAccountToken = &myFalse
	}

	if pj.Spec.DecorationConfig == nil {
		spec.Containers[0].Env = append(spec.Containers[0].Env, kubeEnv(rawEnv)...)
	} else {
		if err := decorate(spec, &pj, rawEnv); err != nil {
			return nil, fmt.Errorf("error decorating podspec: %v", err)
		}
	}

	podLabels, annotations := LabelsAndAnnotationsForJob(pj)
	return &coreapi.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:        pj.ObjectMeta.Name,
			Labels:      podLabels,
			Annotations: annotations,
		},
		Spec: *spec,
	}, nil
}

// ProwJobToPod converts a ProwJob to a Pod that will run the tests.

ProwJobToPod converts a ProwJob to a Pod that will run the tests .


func CloneLogPath(logMount coreapi.VolumeMount) string {
	return filepath.Join(logMount.MountPath, cloneLogPath)
}

func CloneLogPath ( logMount coreapi . VolumeMount ) string { return filepath . Join ( logMount . MountPath , cloneLogPath ) }

// CloneLogPath returns the path to the clone log file in the volume mount.
// CloneLogPath returns the path to the clone log file in the volume mount.

CloneLogPath returns the path to the clone log file in the volume mount . CloneLogPath returns the path to the clone log file in the volume mount .


func cloneEnv(opt clonerefs.Options) ([]coreapi.EnvVar, error) {
	// TODO(fejta): use flags
	cloneConfigEnv, err := clonerefs.Encode(opt)
	if err != nil {
		return nil, err
	}
	return kubeEnv(map[string]string{clonerefs.JSONConfigEnvVar: cloneConfigEnv}), nil
}

func
cloneEnv
(
opt
clonerefs
.
Options
)
(
[
]
coreapi
.
EnvVar
,
error
)
{
// TODO(fejta): use flags
cloneConfigEnv
,
err
:=
clonerefs
.
Encode
(
opt
)
if
err
!=
{
return
,
err
}
return
kubeEnv
(
map
[
string
]
string
{
clonerefs
.
JSONConfigEnvVar
:
cloneConfigEnv
}
)
,
}

// cloneEnv encodes clonerefs Options into json and puts it into an environment variable


func sshVolume(secret string) (coreapi.Volume, coreapi.VolumeMount) {
	var sshKeyMode int32 = 0400 // this is octal, so symbolic ref is `u+r`
	name := strings.Join([]string{"ssh-keys", secret}, "-")
	mountPath := path.Join("/secrets/ssh", secret)
	v := coreapi.Volume{
		Name: name,
		VolumeSource: coreapi.VolumeSource{
			Secret: &coreapi.SecretVolumeSource{
				SecretName:  secret,
				DefaultMode: &sshKeyMode,
			},
		},
	}

	vm := coreapi.VolumeMount{
		Name:      name,
		MountPath: mountPath,
		ReadOnly:  true,
	}

	return v, vm
}

// sshVolume converts a secret holding ssh keys into the corresponding volume and mount.
//
// This is used by CloneRefs to attach the mount to the clonerefs container.

sshVolume converts a secret holding ssh keys into the corresponding volume and mount . This is used by CloneRefs to attach the mount to the clonerefs container .


func InjectEntrypoint(c *coreapi.Container, timeout, gracePeriod time.Duration, prefix, previousMarker string, exitZero bool, log, tools coreapi.VolumeMount) (*wrapper.Options, error) {
	wrapperOptions := &wrapper.Options{
		Args:         append(c.Command, c.Args...),
		ProcessLog:   processLog(log, prefix),
		MarkerFile:   markerFile(log, prefix),
		MetadataFile: metadataFile(log, prefix),
	}
	// TODO(fejta): use flags
	entrypointConfigEnv, err := entrypoint.Encode(entrypoint.Options{
		ArtifactDir:    artifactsDir(log),
		GracePeriod:    gracePeriod,
		Options:        wrapperOptions,
		Timeout:        timeout,
		AlwaysZero:     exitZero,
		PreviousMarker: previousMarker,
	})
	if err != nil {
		return nil, err
	}

	c.Command = []string{entrypointLocation(tools)}
	c.Args = nil
	c.Env = append(c.Env, kubeEnv(map[string]string{entrypoint.JSONConfigEnvVar: entrypointConfigEnv})...)
	c.VolumeMounts = append(c.VolumeMounts, log, tools)
	return wrapperOptions, nil
}

func
InjectEntrypoint
(
c
*
coreapi
.
Container
,
timeout
,
gracePeriod
time
.
Duration
,
prefix
,
previousMarker
string
,
exitZero
bool
,
log
,
tools
coreapi
.
VolumeMount
)
(
*
wrapper
.
Options
,
error
)
{
wrapperOptions
:=
&
wrapper
.
Options
{
Args
:
append
(
c
.
Command
,
c
.
Args
...
)
,
ProcessLog
:
processLog
(
log
,
prefix
)
,
MarkerFile
:
markerFile
(
log
,
prefix
)
,
MetadataFile
:
metadataFile
(
log
,
prefix
)
,
}
// TODO(fejta): use flags
entrypointConfigEnv
,
err
:=
entrypoint
.
Encode
(
entrypoint
.
Options
{
ArtifactDir
:
artifactsDir
(
log
)
,
GracePeriod
:
gracePeriod
,
Options
:
wrapperOptions
,
Timeout
:
timeout
,
AlwaysZero
:
exitZero
,
PreviousMarker
:
previousMarker
,
}
)
if
err
!=
{
return
,
err
}
c
.
Command
=
[
]
string
{
entrypointLocation
(
tools
)
}
c
.
Args
=
c
.
Env
=
append
(
c
.
Env
,
kubeEnv
(
map
[
string
]
string
{
entrypoint
.
JSONConfigEnvVar
:
entrypointConfigEnv
}
)
...
)
c
.
VolumeMounts
=
append
(
c
.
VolumeMounts
,
log
,
tools
)
return
wrapperOptions
,
}

// InjectEntrypoint will make the entrypoint binary in the tools volume the container's entrypoint, which will output to the log volume.

InjectEntrypoint will make the entrypoint binary in the tools volume the container s entrypoint which will output to the log volume .


func PlaceEntrypoint(image string, toolsMount coreapi.VolumeMount) coreapi.Container {
	return coreapi.Container{
		Name:         "place-entrypoint",
		Image:        image,
		Command:      []string{"/bin/cp"},
		Args:         []string{"/entrypoint", entrypointLocation(toolsMount)},
		VolumeMounts: []coreapi.VolumeMount{toolsMount},
	}
}

func PlaceEntrypoint ( image string , toolsMount coreapi . VolumeMount ) coreapi . Container { return coreapi . Container { Name : " " , Image : image , Command : [ ] string { " " } , Args : [ ] string { " " , entrypointLocation ( toolsMount ) } , VolumeMounts : [ ] coreapi . VolumeMount { toolsMount } , } }

// PlaceEntrypoint will copy entrypoint from the entrypoint image to the tools volume


func kubeEnv(environment map[string]string) []coreapi.EnvVar {
	var keys []string
	for key := range environment {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	var kubeEnvironment []coreapi.EnvVar
	for _, key := range keys {
		kubeEnvironment = append(kubeEnvironment, coreapi.EnvVar{
			Name:  key,
			Value: environment[key],
		})
	}

	return kubeEnvironment
}

func kubeEnv ( environment map [ string ] string ) [ ] coreapi . EnvVar { var keys [ ] string for key := range environment { keys = append ( keys , key ) } sort . Strings ( keys ) var kubeEnvironment [ ] coreapi . EnvVar for _ , key := range keys { kubeEnvironment = append ( kubeEnvironment , coreapi . EnvVar { Name : key , Value : environment [ key ] , } ) } return kubeEnvironment }

// kubeEnv transforms a mapping of environment variables
// into their serialized form for a PodSpec, sorting by
// the name of the env vars

kubeEnv transforms a mapping of environment variables into their serialized form for a PodSpec sorting by the name of the env vars


func (o *KubernetesOptions) Client(namespace string, dryRun bool) (*kube.Client, error) {
	if dryRun {
		return kube.NewFakeClient(o.DeckURI), nil
	}

	if o.cluster == "" {
		return kube.NewClientInCluster(namespace)
	}

	return kube.NewClientFromFile(o.cluster, namespace)
}

func ( o * KubernetesOptions ) Client ( namespace string , dryRun bool ) ( * kube . Client , error ) { if dryRun { return kube . NewFakeClient ( o . DeckURI ) , nil } if o . cluster == " " { return kube . NewClientInCluster ( namespace ) } return kube . NewClientFromFile ( o . cluster , namespace ) }


func handle(gc githubClient, le *logrus.Entry, e *event) error {
	needsLabel := e.draft || titleRegex.MatchString(e.title)

	if needsLabel && !e.hasLabel {
		if err := gc.AddLabel(e.org, e.repo, e.number, labels.WorkInProgress); err != nil {
			le.Warnf("error while adding Label %q: %v", labels.WorkInProgress, err)
			return err
		}
	} else if !needsLabel && e.hasLabel {
		if err := gc.RemoveLabel(e.org, e.repo, e.number, labels.WorkInProgress); err != nil {
			le.Warnf("error while removing Label %q: %v", labels.WorkInProgress, err)
			return err
		}
	}
	return nil
}

func handle ( gc githubClient , le * logrus . Entry , e * event ) error { needsLabel := e . draft || titleRegex . MatchString ( e . title ) if needsLabel && ! e . hasLabel { if err := gc . AddLabel ( e . org , e . repo , e . number , labels . WorkInProgress ) ; err != nil { le . Warnf ( " " , labels . WorkInProgress , err ) return err } } else if ! needsLabel && e . hasLabel { if err := gc . RemoveLabel ( e . org , e . repo , e . number , labels . WorkInProgress ) ; err != nil { le . Warnf ( " " , labels . WorkInProgress , err ) return err } } return nil }

// handle interacts with GitHub to drive the pull request to the
// proper state by adding and removing comments and labels. If a
// PR has a WIP prefix, it needs an explanatory comment and label.
// Otherwise, neither should be present.

handle interacts with GitHub to drive the pull request to the proper state by adding and removing comments and labels . If a PR has a WIP prefix it needs an explanatory comment and label . Otherwise neither should be present .


func SendHook(address, eventType string, payload, hmac []byte) error {
	req, err := http.NewRequest(http.MethodPost, address, bytes.NewBuffer(payload))
	if err != nil {
		return err
	}
	req.Header.Set("X-GitHub-Event", eventType)
	req.Header.Set("X-GitHub-Delivery", "GUID")
	req.Header.Set("X-Hub-Signature", github.PayloadSignature(payload, hmac))
	req.Header.Set("content-type", "application/json")

	c := &http.Client{}
	resp, err := c.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	rb, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return fmt.Errorf("response from hook has status %d and body %s", resp.StatusCode, string(bytes.TrimSpace(rb)))
	}
	return nil
}

func SendHook ( address , eventType string , payload , hmac [ ] byte ) error { req , err := http . NewRequest ( http . MethodPost , address , bytes . NewBuffer ( payload ) ) if err != nil { return err } req . Header . Set ( " " , eventType ) req . Header . Set ( " " , " " ) req . Header . Set ( " " , github . PayloadSignature ( payload , hmac ) ) req . Header . Set ( " " , " " ) c := & http . Client { } resp , err := c . Do ( req ) if err != nil { return err } defer resp . Body . Close ( ) rb , err := ioutil . ReadAll ( resp . Body ) if err != nil { return err } if resp . StatusCode != 200 { return fmt . Errorf ( " " , resp . StatusCode , string ( bytes . TrimSpace ( rb ) ) ) } return nil }

// SendHook sends a GitHub event of type eventType to the provided address.

SendHook sends a GitHub event of type eventType to the provided address .


func janitorClean(resource *common.Resource, flags []string) error {
	args := append([]string{fmt.Sprintf("--%s=%s", format(resource.Type), resource.Name)}, flags...)
	logrus.Infof("executing janitor: %s %s", *janitorPath, strings.Join(args, " "))
	cmd := exec.Command(*janitorPath, args...)
	b, err := cmd.CombinedOutput()
	if err != nil {
		logrus.WithError(err).Errorf("failed to clean up project %s, error info: %s", resource.Name, string(b))
	} else {
		logrus.Tracef("output from janitor: %s", string(b))
		logrus.Infof("successfully cleaned up resource %s", resource.Name)
	}
	return err
}

func janitorClean ( resource * common . Resource , flags [ ] string ) error { args := append ( [ ] string { fmt . Sprintf ( " " , format ( resource . Type ) , resource . Name ) } , flags ... ) logrus . Infof ( " " , * janitorPath , strings . Join ( args , " " ) ) cmd := exec . Command ( * janitorPath , args ... ) b , err := cmd . CombinedOutput ( ) if err != nil { logrus . WithError ( err ) . Errorf ( " " , resource . Name , string ( b ) ) } else { logrus . Tracef ( " " , string ( b ) ) logrus . Infof ( " " , resource . Name ) } return err }


func janitor(c boskosClient, buffer <-chan *common.Resource, fn clean, flags []string) {
	for {
		resource := <-buffer

		dest := common.Free
		if err := fn(resource, flags); err != nil {
			logrus.WithError(err).Errorf("%s failed!", *janitorPath)
			dest = common.Dirty
		}

		if err := c.ReleaseOne(resource.Name, dest); err != nil {
			logrus.WithError(err).Error("boskos release failed!")
		}
	}
}

func janitor ( c boskosClient , buffer <- chan * common . Resource , fn clean , flags [ ] string ) { for { resource := <- buffer dest := common . Free if err := fn ( resource , flags ) ; err != nil { logrus . WithError ( err ) . Errorf ( " " , * janitorPath ) dest = common . Dirty } if err := c . ReleaseOne ( resource . Name , dest ) ; err != nil { logrus . WithError ( err ) . Error ( " " ) } } }


func (s *PullServer) Run(ctx context.Context) error {
	configEvent := make(chan config.Delta, 2)
	s.Subscriber.ConfigAgent.Subscribe(configEvent)

	var err error
	defer func() {
		if err != nil {
			logrus.WithError(ctx.Err()).Error("Pull server shutting down")
		}
		logrus.Warn("Pull server shutting down")
	}()
	currentConfig := s.Subscriber.ConfigAgent.Config().PubSubSubscriptions
	errGroup, derivedCtx, err := s.handlePulls(ctx, currentConfig)
	if err != nil {
		return err
	}

	for {
		select {
		// Parent context. Shutdown
		case <-ctx.Done():
			return ctx.Err()
		// Current thread context, it may be failing already
		case <-derivedCtx.Done():
			err = errGroup.Wait()
			return err
		// Checking for update config
		case event := <-configEvent:
			newConfig := event.After.PubSubSubscriptions
			logrus.Info("Received new config")
			if !reflect.DeepEqual(currentConfig, newConfig) {
				logrus.Warn("New config found, reloading pull Server")
				// Making sure the current thread finishes before starting a new one.
				errGroup.Wait()
				// Starting a new thread with new config
				errGroup, derivedCtx, err = s.handlePulls(ctx, newConfig)
				if err != nil {
					return err
				}
				currentConfig = newConfig
			}
		}
	}
}

// Run will block listening to all subscriptions and return once the context is cancelled
// or one of the subscription has a unrecoverable error.

Run will block listening to all subscriptions and return once the context is cancelled or one of the subscription has a unrecoverable error .


func specToStarted(spec *downwardapi.JobSpec, mainRefSHA string) gcs.Started {
	started := gcs.Started{
		Timestamp:   time.Now().Unix(),
		RepoVersion: downwardapi.GetRevisionFromSpec(spec),
	}

	if mainRefSHA != "" {
		started.RepoVersion = mainRefSHA
	}

	// TODO(fejta): VM name

	if spec.Refs != nil && len(spec.Refs.Pulls) > 0 {
		started.Pull = strconv.Itoa(spec.Refs.Pulls[0].Number)
	}

	started.Repos = map[string]string{}

	if spec.Refs != nil {
		started.Repos[spec.Refs.Org+"/"+spec.Refs.Repo] = spec.Refs.String()
	}
	for _, ref := range spec.ExtraRefs {
		started.Repos[ref.Org+"/"+ref.Repo] = ref.String()
	}

	return started
}

func
specToStarted
(
spec
*
downwardapi
.
JobSpec
,
mainRefSHA
string
)
gcs
.
Started
{
started
:=
gcs
.
Started
{
Timestamp
:
time
.
Now
(
)
.
Unix
(
)
,
RepoVersion
:
downwardapi
.
GetRevisionFromSpec
(
spec
)
,
}
if
mainRefSHA
!=
"
"
{
started
.
RepoVersion
=
mainRefSHA
}
// TODO(fejta): VM name
if
spec
.
Refs
!=
&&
len
(
spec
.
Refs
.
Pulls
)
>
0
{
started
.
Pull
=
strconv
.
Itoa
(
spec
.
Refs
.
Pulls
[
0
]
.
Number
)
}
started
.
Repos
=
map
[
string
]
string
{
}
if
spec
.
Refs
!=
{
started
.
Repos
[
spec
.
Refs
.
Org
+
"
"
+
spec
.
Refs
.
Repo
]
=
spec
.
Refs
.
String
(
)
}
for
_
,
ref
:=
range
spec
.
ExtraRefs
{
started
.
Repos
[
ref
.
Org
+
"
"
+
ref
.
Repo
]
=
ref
.
String
(
)
}
return
started
}

// specToStarted translate a jobspec into a started struct
// optionally overwrite RepoVersion with provided mainRefSHA

specToStarted translate a jobspec into a started struct optionally overwrite RepoVersion with provided mainRefSHA


func (o Options) Run() error {
	spec, err := downwardapi.ResolveSpecFromEnv()
	if err != nil {
		return fmt.Errorf("could not resolve job spec: %v", err)
	}

	uploadTargets := map[string]gcs.UploadFunc{}

	var failed bool
	var mainRefSHA string
	if o.Log != "" {
		if failed, mainRefSHA, err = processCloneLog(o.Log, uploadTargets); err != nil {
			return err
		}
	}

	started := specToStarted(spec, mainRefSHA)

	startedData, err := json.Marshal(&started)
	if err != nil {
		return fmt.Errorf("could not marshal starting data: %v", err)
	}

	uploadTargets["started.json"] = gcs.DataUpload(bytes.NewReader(startedData))

	if err := o.Options.Run(spec, uploadTargets); err != nil {
		return fmt.Errorf("failed to upload to GCS: %v", err)
	}

	if failed {
		return errors.New("cloning the appropriate refs failed")
	}

	return nil
}

func ( o Options ) Run ( ) error { spec , err := downwardapi . ResolveSpecFromEnv ( ) if err != nil { return fmt . Errorf ( " " , err ) } uploadTargets := map [ string ] gcs . UploadFunc { } var failed bool var mainRefSHA string if o . Log != " " { if failed , mainRefSHA , err = processCloneLog ( o . Log , uploadTargets ) ; err != nil { return err } } started := specToStarted ( spec , mainRefSHA ) startedData , err := json . Marshal ( & started ) if err != nil { return fmt . Errorf ( " " , err ) } uploadTargets [ " " ] = gcs . DataUpload ( bytes . NewReader ( startedData ) ) if err := o . Options . Run ( spec , uploadTargets ) ; err != nil { return fmt . Errorf ( " " , err ) } if failed { return errors . New ( " " ) } return nil }

// Run will start the initupload job to upload the artifacts, logs and clone status.

Run will start the initupload job to upload the artifacts logs and clone status .


func hasPRChanged(pr github.PullRequestEvent) bool {
	switch pr.Action {
	case github.PullRequestActionOpened:
		return true
	case github.PullRequestActionReopened:
		return true
	case github.PullRequestActionSynchronize:
		return true
	default:
		return false
	}
}

func hasPRChanged ( pr github . PullRequestEvent ) bool { switch pr . Action { case github . PullRequestActionOpened : return true case github . PullRequestActionReopened : return true case github . PullRequestActionSynchronize : return true default : return false } }

// hasPRChanged indicates that the code diff may have changed.

hasPRChanged indicates that the code diff may have changed .


func UpdateIssues(db *gorm.DB, client ClientInterface) {
	latest, err := findLatestIssueUpdate(db, client.RepositoryName())
	if err != nil {
		glog.Error("Failed to find last issue update: ", err)
		return
	}
	c := make(chan *github.Issue, 200)

	go client.FetchIssues(latest, c)
	for issue := range c {
		issueOrm, err := NewIssue(issue, client.RepositoryName())
		if err != nil {
			glog.Error("Can't create issue:", err)
			continue
		}
		if db.Create(issueOrm).Error != nil {
			// We assume record already exists. Let's
			// update. First we need to delete labels and
			// assignees, as they are just concatenated
			// otherwise.
			db.Delete(sql.Label{},
				"issue_id = ? AND repository = ?",
				issueOrm.ID, client.RepositoryName())
			db.Delete(sql.Assignee{},
				"issue_id = ? AND repository = ?",
				issueOrm.ID, client.RepositoryName())

			if err := db.Save(issueOrm).Error; err != nil {
				glog.Error("Failed to update database issue: ", err)
			}
		}

		// Issue is updated, find if we have new comments
		UpdateComments(*issue.Number, issueOrm.IsPR, db, client)
		// and find if we have new events
		UpdateIssueEvents(*issue.Number, db, client)
	}
}

// UpdateIssues downloads new issues and saves in database


func handleReviewEvent(pc plugins.Agent, re github.ReviewEvent) error {
	return handleReview(
		pc.Logger,
		pc.GitHubClient,
		pc.OwnersClient,
		pc.Config.GitHubOptions,
		pc.PluginConfig,
		&re,
	)
}

func handleReviewEvent ( pc plugins . Agent , re github . ReviewEvent ) error { return handleReview ( pc . Logger , pc . GitHubClient , pc . OwnersClient , pc . Config . GitHubOptions , pc . PluginConfig , & re , ) }

// handleReviewEvent should only handle reviews that have no approval command.
// Reviews with approval commands will be handled by handleGenericCommentEvent.

handleReviewEvent should only handle reviews that have no approval command . Reviews with approval commands will be handled by handleGenericCommentEvent .


func findAssociatedIssue(body, org string) (int, error) {
	associatedIssueRegex, err := regexp.Compile(fmt.Sprintf(associatedIssueRegexFormat, org))
	if err != nil {
		return 0, err
	}
	match := associatedIssueRegex.FindStringSubmatch(body)
	if len(match) == 0 {
		return 0, nil
	}
	v, err := strconv.Atoi(match[1])
	if err != nil {
		return 0, err
	}
	return v, nil
}

func findAssociatedIssue ( body , org string ) ( int , error ) { associatedIssueRegex , err := regexp . Compile ( fmt . Sprintf ( associatedIssueRegexFormat , org ) ) if err != nil { return 0 , err } match := associatedIssueRegex . FindStringSubmatch ( body ) if len ( match ) == 0 { return 0 , nil } v , err := strconv . Atoi ( match [ 1 ] ) if err != nil { return 0 , err } return v , nil }

// Returns associated issue, or 0 if it can't find any.
// This is really simple, and could be improved later.

Returns associated issue or 0 if it can t find any . This is really simple and could be improved later .


func optionsForRepo(config *plugins.Configuration, org, repo string) *plugins.Approve {
	fullName := fmt.Sprintf("%s/%s", org, repo)

	a := func() *plugins.Approve {
		// First search for repo config
		for _, c := range config.Approve {
			if !strInSlice(fullName, c.Repos) {
				continue
			}
			return &c
		}

		// If you don't find anything, loop again looking for an org config
		for _, c := range config.Approve {
			if !strInSlice(org, c.Repos) {
				continue
			}
			return &c
		}

		// Return an empty config, and use plugin defaults
		return &plugins.Approve{}
	}()
	if a.DeprecatedImplicitSelfApprove == nil && a.RequireSelfApproval == nil && config.UseDeprecatedSelfApprove {
		no := false
		a.DeprecatedImplicitSelfApprove = &no
	}
	if a.DeprecatedReviewActsAsApprove == nil && a.IgnoreReviewState == nil && config.UseDeprecatedReviewApprove {
		no := false
		a.DeprecatedReviewActsAsApprove = &no
	}
	return a
}

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

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


func localOnlyMain(cfg config.Getter, o options, mux *http.ServeMux) *http.ServeMux {
	mux.Handle("/github-login", gziphandler.GzipHandler(handleSimpleTemplate(o, cfg, "github-login.html", nil)))

	if o.spyglass {
		initSpyglass(cfg, o, mux, nil)
	}

	return mux
}

func localOnlyMain ( cfg config . Getter , o options , mux * http . ServeMux ) * http . ServeMux { mux . Handle ( " " , gziphandler . GzipHandler ( handleSimpleTemplate ( o , cfg , " " , nil ) ) ) if o . spyglass { initSpyglass ( cfg , o , mux , nil ) } return mux }

// localOnlyMain contains logic used only when running locally, and is mutually exclusive with
// prodOnlyMain.

localOnlyMain contains logic used only when running locally and is mutually exclusive with prodOnlyMain .


func (covList *CoverageList) summarize() {
	covList.NumCoveredStmts = 0
	covList.NumAllStmts = 0
	for _, item := range covList.Group {
		covList.NumCoveredStmts += item.NumCoveredStmts
		covList.NumAllStmts += item.NumAllStmts
	}
}

func ( covList * CoverageList ) summarize ( ) { covList . NumCoveredStmts = 0 covList . NumAllStmts = 0 for _ , item := range covList . Group { covList . NumCoveredStmts += item . NumCoveredStmts covList . NumAllStmts += item . NumAllStmts } }

// summarize summarizes all items in the Group and stores the result


func (covList *CoverageList) Subset(prefix string) *CoverageList {
	s := newCoverageList("Filtered Summary")
	for _, c := range covList.Group {
		if strings.HasPrefix(c.Name, prefix) {
			covList.Group = append(covList.Group, c)
		}
	}
	return s
}

func ( covList * CoverageList ) Subset ( prefix string ) * CoverageList { s := newCoverageList ( " " ) for _ , c := range covList . Group { if strings . HasPrefix ( c . Name , prefix ) { covList . Group = append ( covList . Group , c ) } } return s }

// Subset returns the subset obtained through applying filter


func (covList CoverageList) ListDirectories() []string {
	dirSet := map[string]bool{}
	for _, cov := range covList.Group {
		dirSet[path.Dir(cov.Name)] = true
	}
	var result []string
	for key := range dirSet {
		result = append(result, key)
	}
	return result
}

func ( covList CoverageList ) ListDirectories ( ) [ ] string { dirSet := map [ string ] bool { } for _ , cov := range covList . Group { dirSet [ path . Dir ( cov . Name ) ] = true } var result [ ] string for key := range dirSet { result = append ( result , key ) } return result }

// ListDirectories gets a list a sub-directories that contains source code.

ListDirectories gets a list a sub - directories that contains source code .


func readRequest(r io.Reader, contentType string) (*admissionapi.AdmissionRequest, error) {
	if contentType != contentTypeJSON {
		return nil, fmt.Errorf("Content-Type=%s, expected %s", contentType, contentTypeJSON)
	}

	// Can we read the body?
	if r == nil {
		return nil, fmt.Errorf("no body")
	}
	body, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, fmt.Errorf("read body: %v", err)
	}

	// Can we convert the body into an AdmissionReview?
	var ar admissionapi.AdmissionReview
	deserializer := codecs.UniversalDeserializer()
	if _, _, err := deserializer.Decode(body, nil, &ar); err != nil {
		return nil, fmt.Errorf("decode body: %v", err)
	}
	return ar.Request, nil
}

// readRequest extracts the request from the AdmissionReview reader


func handle(w http.ResponseWriter, r *http.Request) {
	req, err := readRequest(r.Body, r.Header.Get("Content-Type"))
	if err != nil {
		logrus.WithError(err).Error("read")
	}

	if err := writeResponse(*req, w, onlyUpdateStatus); err != nil {
		logrus.WithError(err).Error("write")
	}
}

func handle ( w http . ResponseWriter , r * http . Request ) { req , err := readRequest ( r . Body , r . Header . Get ( " " ) ) if err != nil { logrus . WithError ( err ) . Error ( " " ) } if err := writeResponse ( * req , w , onlyUpdateStatus ) ; err != nil { logrus . WithError ( err ) . Error ( " " ) } }

// handle reads the request and writes the response


func writeResponse(ar admissionapi.AdmissionRequest, w io.Writer, decide decider) error {
	response, err := decide(ar)
	if err != nil {
		logrus.WithError(err).Error("failed decision")
		response = &admissionapi.AdmissionResponse{
			Result: &meta.Status{
				Message: err.Error(),
			},
		}
	}
	var result admissionapi.AdmissionReview
	result.Response = response
	result.Response.UID = ar.UID
	out, err := json.Marshal(result)
	if err != nil {
		return fmt.Errorf("encode response: %v", err)
	}
	if _, err := w.Write(out); err != nil {
		return fmt.Errorf("write response: %v", err)
	}
	return nil
}

func writeResponse ( ar admissionapi . AdmissionRequest , w io . Writer , decide decider ) error { response , err := decide ( ar ) if err != nil { logrus . WithError ( err ) . Error ( " " ) response = & admissionapi . AdmissionResponse { Result : & meta . Status { Message : err . Error ( ) , } , } } var result admissionapi . AdmissionReview result . Response = response result . Response . UID = ar . UID out , err := json . Marshal ( result ) if err != nil { return fmt . Errorf ( " " , err ) } if _ , err := w . Write ( out ) ; err != nil { return fmt . Errorf ( " " , err ) } return nil }

// writeResponse gets the response from onlyUpdateStatus and writes it to w.

writeResponse gets the response from onlyUpdateStatus and writes it to w .


func onlyUpdateStatus(req admissionapi.AdmissionRequest) (*admissionapi.AdmissionResponse, error) {
	logger := logrus.WithFields(logrus.Fields{
		"resource":    req.Resource,
		"subresource": req.SubResource,
		"name":        req.Name,
		"namespace":   req.Namespace,
		"operation":   req.Operation,
	})

	// Does this only update status?
	if req.SubResource == "status" {
		logrus.Info("accept status update")
		return &allow, nil
	}

	// Otherwise, do the specs match?
	var new prowjobv1.ProwJob
	if _, _, err := codecs.UniversalDeserializer().Decode(req.Object.Raw, nil, &new); err != nil {
		return nil, fmt.Errorf("decode new: %v", err)
	}
	var old prowjobv1.ProwJob
	if _, _, err := codecs.UniversalDeserializer().Decode(req.OldObject.Raw, nil, &old); err != nil {
		return nil, fmt.Errorf("decode old: %v", err)
	}
	if equality.Semantic.DeepEqual(old.Spec, new.Spec) {
		logrus.Info("accept update with equivalent spec")
		return &allow, nil // yes
	}
	logger.Info("reject") // no
	return &reject, nil
}

func
onlyUpdateStatus
(
req
admissionapi
.
AdmissionRequest
)
(
*
admissionapi
.
AdmissionResponse
,
error
)
{
logger
:=
logrus
.
WithFields
(
logrus
.
Fields
{
"
"
:
req
.
Resource
,
"
"
:
req
.
SubResource
,
"
"
:
req
.
Name
,
"
"
:
req
.
Namespace
,
"
"
:
req
.
Operation
,
}
)
// Does this only update status?
if
req
.
SubResource
==
"
"
{
logrus
.
Info
(
"
"
)
return
&
allow
,
}
// Otherwise, do the specs match?
var
new
prowjobv1
.
ProwJob
if
_
,
_
,
err
:=
codecs
.
UniversalDeserializer
(
)
.
Decode
(
req
.
Object
.
Raw
,
,
&
new
)
;
err
!=
{
return
,
fmt
.
Errorf
(
"
"
,
err
)
}
var
old
prowjobv1
.
ProwJob
if
_
,
_
,
err
:=
codecs
.
UniversalDeserializer
(
)
.
Decode
(
req
.
OldObject
.
Raw
,
,
&
old
)
;
err
!=
{
return
,
fmt
.
Errorf
(
"
"
,
err
)
}
if
equality
.
Semantic
.
DeepEqual
(
old
.
Spec
,
new
.
Spec
)
{
logrus
.
Info
(
"
"
)
return
&
allow
,
// yes
}
logger
.
Info
(
"
"
)
// no
return
&
reject
,
}

// onlyUpdateStatus returns the response to the request


func convertSuiteMeta(suiteMeta gcs.SuitesMeta) resultstore.Suite {
	out := resultstore.Suite{
		Name: path.Base(suiteMeta.Path),
		Files: []resultstore.File{
			{
				ContentType: "text/xml",
				ID:          resultstore.UUID(),
				URL:         suiteMeta.Path, // ensure the junit.xml file appears in artifacts list
			},
		},
	}
	for _, suite := range suiteMeta.Suites.Suites {
		child := resultstore.Suite{
			Name:     suite.Name,
			Duration: dur(suite.Time),
		}
		switch {
		case suite.Failures > 0 && suite.Tests >= suite.Failures:
			child.Failures = append(child.Failures, resultstore.Failure{
				Message: fmt.Sprintf("%d out of %d tests failed (%.1f%% passing)", suite.Failures, suite.Tests, float64(suite.Tests-suite.Failures)*100.0/float64(suite.Tests)),
			})
		case suite.Failures > 0:
			child.Failures = append(child.Failures, resultstore.Failure{
				Message: fmt.Sprintf("%d tests failed", suite.Failures),
			})
		}
		for _, result := range suite.Results {
			name, tags := stripTags(result.Name)
			class := result.ClassName
			if class == "" {
				class = strings.Join(tags, " ")
			} else {
				class += " " + strings.Join(tags, " ")
			}
			c := resultstore.Case{
				Name:     name,
				Class:    class,
				Duration: dur(result.Time),
				Result:   resultstore.Completed,
			}
			const max = 5000 // truncate messages to this length
			msg := result.Message(max)
			switch {
			case result.Failure != nil:
				// failing tests have a completed result with an error
				if msg == "" {
					msg = "unknown failure"
				}
				c.Failures = append(c.Failures, resultstore.Failure{
					Message: msg,
				})
			case result.Skipped != nil:
				c.Result = resultstore.Skipped
				if msg != "" { // skipped results do not require an error, but may.
					c.Errors = append(c.Errors, resultstore.Error{
						Message: msg,
					})
				}
			}
			child.Cases = append(child.Cases, c)
			if c.Duration > child.Duration {
				child.Duration = c.Duration
			}
		}
		if child.Duration > out.Duration {
			// Assume suites run in parallel, so choose max
			out.Duration = child.Duration
		}
		out.Suites = append(out.Suites, child)
	}
	return out
}

// convertSuiteMeta converts a junit result in gcs to a ResultStore Suite.

convertSuiteMeta converts a junit result in gcs to a ResultStore Suite .


func NewHealth() *Health {
	healthMux := http.NewServeMux()
	healthMux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "OK") })
	go func() {
		logrus.WithError(http.ListenAndServe(":"+strconv.Itoa(healthPort), healthMux)).Fatal("ListenAndServe returned.")
	}()
	return &Health{
		healthMux: healthMux,
	}
}

func NewHealth ( ) * Health { healthMux := http . NewServeMux ( ) healthMux . HandleFunc ( " " , func ( w http . ResponseWriter , r * http . Request ) { fmt . Fprint ( w , " " ) } ) go func ( ) { logrus . WithError ( http . ListenAndServe ( " " + strconv . Itoa ( healthPort ) , healthMux ) ) . Fatal ( " " ) } ( ) return & Health { healthMux : healthMux , } }

// NewHealth creates a new health request multiplexer and starts serving the liveness endpoint
// on the given port

NewHealth creates a new health request multiplexer and starts serving the liveness endpoint on the given port


func (h *Health) ServeReady() {
	h.healthMux.HandleFunc("/healthz/ready", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "OK") })
}

func ( h * Health ) ServeReady ( ) { h . healthMux . HandleFunc ( " " , func ( w http . ResponseWriter , r * http . Request ) { fmt . Fprint ( w , " " ) } ) }

// ServeReady starts serving the readiness endpoint


func NewController(ghcSync, ghcStatus *github.Client, prowJobClient prowv1.ProwJobInterface, cfg config.Getter, gc *git.Client, maxRecordsPerPool int, opener io.Opener, historyURI, statusURI string, logger *logrus.Entry) (*Controller, error) {
	if logger == nil {
		logger = logrus.NewEntry(logrus.StandardLogger())
	}
	hist, err := history.New(maxRecordsPerPool, opener, historyURI)
	if err != nil {
		return nil, fmt.Errorf("error initializing history client from %q: %v", historyURI, err)
	}
	sc := &statusController{
		logger:         logger.WithField("controller", "status-update"),
		ghc:            ghcStatus,
		config:         cfg,
		newPoolPending: make(chan bool, 1),
		shutDown:       make(chan bool),
		opener:         opener,
		path:           statusURI,
	}
	go sc.run()
	return &Controller{
		logger:        logger.WithField("controller", "sync"),
		ghc:           ghcSync,
		prowJobClient: prowJobClient,
		config:        cfg,
		gc:            gc,
		sc:            sc,
		changedFiles: &changedFilesAgent{
			ghc:             ghcSync,
			nextChangeCache: make(map[changeCacheKey][]string),
		},
		History: hist,
	}, nil
}

func NewController ( ghcSync , ghcStatus * github . Client , prowJobClient prowv1 . ProwJobInterface , cfg config . Getter , gc * git . Client , maxRecordsPerPool int , opener io . Opener , historyURI , statusURI string , logger * logrus . Entry ) ( * Controller , error ) { if logger == nil { logger = logrus . NewEntry ( logrus . StandardLogger ( ) ) } hist , err := history . New ( maxRecordsPerPool , opener , historyURI ) if err != nil { return nil , fmt . Errorf ( " " , historyURI , err ) } sc := & statusController { logger : logger . WithField ( " " , " " ) , ghc : ghcStatus , config : cfg , newPoolPending : make ( chan bool , 1 ) , shutDown : make ( chan bool ) , opener : opener , path : statusURI , } go sc . run ( ) return & Controller { logger : logger . WithField ( " " , " " ) , ghc : ghcSync , prowJobClient : prowJobClient , config : cfg , gc : gc , sc : sc , changedFiles : & changedFilesAgent { ghc : ghcSync , nextChangeCache : make ( map [ changeCacheKey ] [ ] string ) , } , History : hist , } , nil }

// NewController makes a Controller out of the given clients.

NewController makes a Controller out of the given clients .


func newExpectedContext(c string) Context {
	return Context{
		Context:     githubql.String(c),
		State:       githubql.StatusStateExpected,
		Description: githubql.String(""),
	}
}

func newExpectedContext ( c string ) Context { return Context { Context : githubql . String ( c ) , State : githubql . StatusStateExpected , Description : githubql . String ( " " ) , } }

// newExpectedContext creates a Context with Expected state.

newExpectedContext creates a Context with Expected state .


func contextsToStrings(contexts []Context) []string {
	var names []string
	for _, c := range contexts {
		names = append(names, string(c.Context))
	}
	return names
}

func contextsToStrings ( contexts [ ] Context ) [ ] string { var names [ ] string for _ , c := range contexts { names = append ( names , string ( c . Context ) ) } return names }

// contextsToStrings converts a list Context to a list of string


func (c *Controller) filterSubpools(goroutines int, raw map[string]*subpool) map[string]*subpool {
	filtered := make(map[string]*subpool)
	var lock sync.Mutex

	subpoolsInParallel(
		goroutines,
		raw,
		func(sp *subpool) {
			if err := c.initSubpoolData(sp); err != nil {
				sp.log.WithError(err).Error("Error initializing subpool.")
				return
			}
			key := poolKey(sp.org, sp.repo, sp.branch)
			if spFiltered := filterSubpool(c.ghc, sp); spFiltered != nil {
				sp.log.WithField("key", key).WithField("pool", spFiltered).Debug("filtered sub-pool")

				lock.Lock()
				filtered[key] = spFiltered
				lock.Unlock()
			} else {
				sp.log.WithField("key", key).WithField("pool", spFiltered).Debug("filtering sub-pool removed all PRs")
			}
		},
	)
	return filtered
}

func ( c * Controller ) filterSubpools ( goroutines int , raw map [ string ] * subpool ) map [ string ] * subpool { filtered := make ( map [ string ] * subpool ) var lock sync . Mutex subpoolsInParallel ( goroutines , raw , func ( sp * subpool ) { if err := c . initSubpoolData ( sp ) ; err != nil { sp . log . WithError ( err ) . Error ( " " ) return } key := poolKey ( sp . org , sp . repo , sp . branch ) if spFiltered := filterSubpool ( c . ghc , sp ) ; spFiltered != nil { sp . log . WithField ( " " , key ) . WithField ( " " , spFiltered ) . Debug ( " " ) lock . Lock ( ) filtered [ key ] = spFiltered lock . Unlock ( ) } else { sp . log . WithField ( " " , key ) . WithField ( " " , spFiltered ) . Debug ( " " ) } } , ) return filtered }

// filterSubpools filters non-pool PRs out of the initially identified subpools,
// deleting any pools that become empty.
// See filterSubpool for filtering details.

filterSubpools filters non - pool PRs out of the initially identified subpools deleting any pools that become empty . See filterSubpool for filtering details .


func filterSubpool(ghc githubClient, sp *subpool) *subpool {
	var toKeep []PullRequest
	for _, pr := range sp.prs {
		if !filterPR(ghc, sp, &pr) {
			toKeep = append(toKeep, pr)
		}
	}
	if len(toKeep) == 0 {
		return nil
	}
	sp.prs = toKeep
	return sp
}

func filterSubpool ( ghc githubClient , sp * subpool ) * subpool { var toKeep [ ] PullRequest for _ , pr := range sp . prs { if ! filterPR ( ghc , sp , & pr ) { toKeep = append ( toKeep , pr ) } } if len ( toKeep ) == 0 { return nil } sp . prs = toKeep return sp }

// filterSubpool filters PRs from an initially identified subpool, returning the
// filtered subpool.
// If the subpool becomes empty 'nil' is returned to indicate that the subpool
// should be deleted.

filterSubpool filters PRs from an initially identified subpool returning the filtered subpool . If the subpool becomes empty nil is returned to indicate that the subpool should be deleted .


func poolPRMap(subpoolMap map[string]*subpool) map[string]PullRequest {
	prs := make(map[string]PullRequest)
	for _, sp := range subpoolMap {
		for _, pr := range sp.prs {
			prs[prKey(&pr)] = pr
		}
	}
	return prs
}

func poolPRMap ( subpoolMap map [ string ] * subpool ) map [ string ] PullRequest { prs := make ( map [ string ] PullRequest ) for _ , sp := range subpoolMap { for _ , pr := range sp . prs { prs [ prKey ( & pr ) ] = pr } } return prs }

// poolPRMap collects all subpool PRs into a map containing all pooled PRs.

poolPRMap collects all subpool PRs into a map containing all pooled PRs .


func unsuccessfulContexts(contexts []Context, cc contextChecker, log *logrus.Entry) []Context {
	var failed []Context
	for _, ctx := range contexts {
		if string(ctx.Context) == statusContext {
			continue
		}
		if cc.IsOptional(string(ctx.Context)) {
			continue
		}
		if ctx.State != githubql.StatusStateSuccess {
			failed = append(failed, ctx)
		}
	}
	for _, c := range cc.MissingRequiredContexts(contextsToStrings(contexts)) {
		failed = append(failed, newExpectedContext(c))
	}

	log.Debugf("from %d total contexts (%v) found %d failing contexts: %v", len(contexts), contextsToStrings(contexts), len(failed), contextsToStrings(failed))
	return failed
}

func unsuccessfulContexts ( contexts [ ] Context , cc contextChecker , log * logrus . Entry ) [ ] Context { var failed [ ] Context for _ , ctx := range contexts { if string ( ctx . Context ) == statusContext { continue } if cc . IsOptional ( string ( ctx . Context ) ) { continue } if ctx . State != githubql . StatusStateSuccess { failed = append ( failed , ctx ) } } for _ , c := range cc . MissingRequiredContexts ( contextsToStrings ( contexts ) ) { failed = append ( failed , newExpectedContext ( c ) ) } log . Debugf ( " " , len ( contexts ) , contextsToStrings ( contexts ) , len ( failed ) , contextsToStrings ( failed ) ) return failed }

// unsuccessfulContexts determines which contexts from the list that we care about are
// failed. For instance, we do not care about our own context.
// If the branchProtection is set to only check for required checks, we will skip
// all non-required tests. If required tests are missing from the list, they will be
// added to the list of failed contexts.

unsuccessfulContexts determines which contexts from the list that we care about are failed . For instance we do not care about our own context . If the branchProtection is set to only check for required checks we will skip all non - required tests . If required tests are missing from the list they will be added to the list of failed contexts .


func accumulate(presubmits map[int][]config.Presubmit, prs []PullRequest, pjs []prowapi.ProwJob, log *logrus.Entry) (successes, pendings, nones []PullRequest) {
	for _, pr := range prs {
		// Accumulate the best result for each job.
		psStates := make(map[string]simpleState)
		for _, pj := range pjs {
			if pj.Spec.Type != prowapi.PresubmitJob {
				continue
			}
			if pj.Spec.Refs.Pulls[0].Number != int(pr.Number) {
				continue
			}
			if pj.Spec.Refs.Pulls[0].SHA != string(pr.HeadRefOID) {
				continue
			}

			name := pj.Spec.Context
			oldState := psStates[name]
			newState := toSimpleState(pj.Status.State)
			if oldState == failureState || oldState == "" {
				psStates[name] = newState
			} else if oldState == pendingState && newState == successState {
				psStates[name] = successState
			}
		}
		// The overall result is the worst of the best.
		overallState := successState
		for _, ps := range presubmits[int(pr.Number)] {
			if s, ok := psStates[ps.Context]; !ok {
				overallState = failureState
				log.WithFields(pr.logFields()).Debugf("missing presubmit %s", ps.Context)
				break
			} else if s == failureState {
				overallState = failureState
				log.WithFields(pr.logFields()).Debugf("presubmit %s not passing", ps.Context)
				break
			} else if s == pendingState {
				log.WithFields(pr.logFields()).Debugf("presubmit %s pending", ps.Context)
				overallState = pendingState
			}
		}
		if overallState == successState {
			successes = append(successes, pr)
		} else if overallState == pendingState {
			pendings = append(pendings, pr)
		} else {
			nones = append(nones, pr)
		}
	}
	return
}

// accumulate returns the supplied PRs sorted into three buckets based on their
// accumulated state across the presubmits.

accumulate returns the supplied PRs sorted into three buckets based on their accumulated state across the presubmits .


func tryMerge(mergeFunc func() error) (bool, error) {
	var err error
	const maxRetries = 3
	backoff := time.Second * 4
	for retry := 0; retry < maxRetries; retry++ {
		if err = mergeFunc(); err == nil {
			// Successful merge!
			return true, nil
		}
		// TODO: Add a config option to abort batches if a PR in the batch
		// cannot be merged for any reason. This would skip merging
		// not just the changed PR, but also the other PRs in the batch.
		// This shouldn't be the default behavior as merging batches is high
		// priority and this is unlikely to be problematic.
		// Note: We would also need to be able to roll back any merges for the
		// batch that were already successfully completed before the failure.
		// Ref: https://github.com/kubernetes/test-infra/issues/10621
		if _, ok := err.(github.ModifiedHeadError); ok {
			// This is a possible source of incorrect behavior. If someone
			// modifies their PR as we try to merge it in a batch then we
			// end up in an untested state. This is unlikely to cause any
			// real problems.
			return true, fmt.Errorf("PR was modified: %v", err)
		} else if _, ok = err.(github.UnmergablePRBaseChangedError); ok {
			//  complained that the base branch was modified. This is a
			// strange error because the API doesn't even allow the request to
			// specify the base branch sha, only the head sha.
			// We suspect that github is complaining because we are making the
			// merge requests too rapidly and it cannot recompute mergability
			// in time. https://github.com/kubernetes/test-infra/issues/5171
			// We handle this by sleeping for a few seconds before trying to
			// merge again.
			err = fmt.Errorf("base branch was modified: %v", err)
			if retry+1 < maxRetries {
				sleep(backoff)
				backoff *= 2
			}
		} else if _, ok = err.(github.UnauthorizedToPushError); ok {
			// GitHub let us know that the token used cannot push to the branch.
			// Even if the robot is set up to have write access to the repo, an
			// overzealous branch protection setting will not allow the robot to
			// push to a specific branch.
			// We won't be able to merge the other PRs.
			return false, fmt.Errorf("branch needs to be configured to allow this robot to push: %v", err)
		} else if _, ok = err.(github.MergeCommitsForbiddenError); ok {
			// GitHub let us know that the merge method configured for this repo
			// is not allowed by other repo settings, so we should let the admins
			// know that the configuration needs to be updated.
			// We won't be able to merge the other PRs.
			return false, fmt.Errorf("Tide needs to be configured to use the 'rebase' merge method for this repo or the repo needs to allow merge commits: %v", err)
		} else if _, ok = err.(github.UnmergablePRError); ok {
			return true, fmt.Errorf("PR is unmergable. Do the Tide merge requirements match the GitHub settings for the repo? %v", err)
		} else {
			return true, err
		}
	}
	// We ran out of retries. Return the last transient error.
	return true, err
}

// tryMerge attempts 1 merge and returns a bool indicating if we should try
// to merge the remaining PRs and possibly an error.

tryMerge attempts 1 merge and returns a bool indicating if we should try to merge the remaining PRs and possibly an error .


func (c *changedFilesAgent) prChanges(pr *PullRequest) config.ChangedFilesProvider {
	return func() ([]string, error) {
		cacheKey := changeCacheKey{
			org:    string(pr.Repository.Owner.Login),
			repo:   string(pr.Repository.Name),
			number: int(pr.Number),
			sha:    string(pr.HeadRefOID),
		}

		c.RLock()
		changedFiles, ok := c.changeCache[cacheKey]
		if ok {
			c.RUnlock()
			c.Lock()
			c.nextChangeCache[cacheKey] = changedFiles
			c.Unlock()
			return changedFiles, nil
		}
		if changedFiles, ok = c.nextChangeCache[cacheKey]; ok {
			c.RUnlock()
			return changedFiles, nil
		}
		c.RUnlock()

		// We need to query the changes from GitHub.
		changes, err := c.ghc.GetPullRequestChanges(
			string(pr.Repository.Owner.Login),
			string(pr.Repository.Name),
			int(pr.Number),
		)
		if err != nil {
			return nil, fmt.Errorf("error getting PR changes for #%d: %v", int(pr.Number), err)
		}
		changedFiles = make([]string, 0, len(changes))
		for _, change := range changes {
			changedFiles = append(changedFiles, change.Filename)
		}

		c.Lock()
		c.nextChangeCache[cacheKey] = changedFiles
		c.Unlock()
		return changedFiles, nil
	}
}

// prChanges gets the files changed by the PR, either from the cache or by
// querying GitHub.

prChanges gets the files changed by the PR either from the cache or by querying GitHub .


func (c *changedFilesAgent) prune() {
	c.Lock()
	defer c.Unlock()
	c.changeCache = c.nextChangeCache
	c.nextChangeCache = make(map[changeCacheKey][]string)
}

func ( c * changedFilesAgent ) prune ( ) { c . Lock ( ) defer c . Unlock ( ) c . changeCache = c . nextChangeCache c . nextChangeCache = make ( map [ changeCacheKey ] [ ] string ) }

// prune removes any cached file changes that were not used since the last prune.

prune removes any cached file changes that were not used since the last prune .


func (c *Controller) dividePool(pool map[string]PullRequest, pjs []prowapi.ProwJob) (map[string]*subpool, error) {
	sps := make(map[string]*subpool)
	for _, pr := range pool {
		org := string(pr.Repository.Owner.Login)
		repo := string(pr.Repository.Name)
		branch := string(pr.BaseRef.Name)
		branchRef := string(pr.BaseRef.Prefix) + string(pr.BaseRef.Name)
		fn := poolKey(org, repo, branch)
		if sps[fn] == nil {
			sha, err := c.ghc.GetRef(org, repo, strings.TrimPrefix(branchRef, "refs/"))
			if err != nil {
				return nil, err
			}
			sps[fn] = &subpool{
				log: c.logger.WithFields(logrus.Fields{
					"org":      org,
					"repo":     repo,
					"branch":   branch,
					"base-sha": sha,
				}),
				org:    org,
				repo:   repo,
				branch: branch,
				sha:    sha,
			}
		}
		sps[fn].prs = append(sps[fn].prs, pr)
	}
	for _, pj := range pjs {
		if pj.Spec.Type != prowapi.PresubmitJob && pj.Spec.Type != prowapi.BatchJob {
			continue
		}
		fn := poolKey(pj.Spec.Refs.Org, pj.Spec.Refs.Repo, pj.Spec.Refs.BaseRef)
		if sps[fn] == nil || pj.Spec.Refs.BaseSHA != sps[fn].sha {
			continue
		}
		sps[fn].pjs = append(sps[fn].pjs, pj)
	}
	return sps, nil
}

func ( c * Controller ) dividePool ( pool map [ string ] PullRequest , pjs [ ] prowapi . ProwJob ) ( map [ string ] * subpool , error ) { sps := make ( map [ string ] * subpool ) for _ , pr := range pool { org := string ( pr . Repository . Owner . Login ) repo := string ( pr . Repository . Name ) branch := string ( pr . BaseRef . Name ) branchRef := string ( pr . BaseRef . Prefix ) + string ( pr . BaseRef . Name ) fn := poolKey ( org , repo , branch ) if sps [ fn ] == nil { sha , err := c . ghc . GetRef ( org , repo , strings . TrimPrefix ( branchRef , " " ) ) if err != nil { return nil , err } sps [ fn ] = & subpool { log : c . logger . WithFields ( logrus . Fields { " " : org , " " : repo , " " : branch , " " : sha , } ) , org : org , repo : repo , branch : branch , sha : sha , } } sps [ fn ] . prs = append ( sps [ fn ] . prs , pr ) } for _ , pj := range pjs { if pj . Spec . Type != prowapi . PresubmitJob && pj . Spec . Type != prowapi . BatchJob { continue } fn := poolKey ( pj . Spec . Refs . Org , pj . Spec . Refs . Repo , pj . Spec . Refs . BaseRef ) if sps [ fn ] == nil || pj . Spec . Refs . BaseSHA != sps [ fn ] . sha { continue } sps [ fn ] . pjs = append ( sps [ fn ] . pjs , pj ) } return sps , nil }

// dividePool splits up the list of pull requests and prow jobs into a group
// per repo and branch. It only keeps ProwJobs that match the latest branch.

dividePool splits up the list of pull requests and prow jobs into a group per repo and branch . It only keeps ProwJobs that match the latest branch .


func AggregateProfiles(profiles [][]*cover.Profile) ([]*cover.Profile, error) {
	setProfiles := make([][]*cover.Profile, 0, len(profiles))
	for _, p := range profiles {
		c := countToBoolean(p)
		setProfiles = append(setProfiles, c)
	}
	aggregateProfiles, err := MergeMultipleProfiles(setProfiles)
	if err != nil {
		return nil, err
	}
	return aggregateProfiles, nil
}

func AggregateProfiles ( profiles [ ] [ ] * cover . Profile ) ( [ ] * cover . Profile , error ) { setProfiles := make ( [ ] [ ] * cover . Profile , 0 , len ( profiles ) ) for _ , p := range profiles { c := countToBoolean ( p ) setProfiles = append ( setProfiles , c ) } aggregateProfiles , err := MergeMultipleProfiles ( setProfiles ) if err != nil { return nil , err } return aggregateProfiles , nil }

// AggregateProfiles takes multiple coverage profiles and produces a new
// coverage profile that counts the number of profiles that hit a block at least
// once.

AggregateProfiles takes multiple coverage profiles and produces a new coverage profile that counts the number of profiles that hit a block at least once .


func countToBoolean(profile []*cover.Profile) []*cover.Profile {
	setProfile := make([]*cover.Profile, 0, len(profile))
	for _, p := range profile {
		pc := deepCopyProfile(*p)
		for i := range pc.Blocks {
			if pc.Blocks[i].Count > 0 {
				pc.Blocks[i].Count = 1
			}
		}
		setProfile = append(setProfile, &pc)
	}
	return setProfile
}

func countToBoolean ( profile [ ] * cover . Profile ) [ ] * cover . Profile { setProfile := make ( [ ] * cover . Profile , 0 , len ( profile ) ) for _ , p := range profile { pc := deepCopyProfile ( * p ) for i := range pc . Blocks { if pc . Blocks [ i ] . Count > 0 { pc . Blocks [ i ] . Count = 1 } } setProfile = append ( setProfile , & pc ) } return setProfile }

// countToBoolean converts a profile containing hit counts to instead contain
// only 1s or 0s.

countToBoolean converts a profile containing hit counts to instead contain only 1s or 0s .


func NewStorage(r storage.PersistenceLayer, storage string) (*Storage, error) {
	s := &Storage{
		resources: r,
	}

	if storage != "" {
		var data struct {
			Resources []common.Resource
		}
		buf, err := ioutil.ReadFile(storage)
		if err == nil {
			logrus.Infof("Current state: %s.", string(buf))
			err = json.Unmarshal(buf, &data)
			if err != nil {
				return nil, err
			}
		} else if !os.IsNotExist(err) {
			return nil, err
		}

		logrus.Info("Before adding resource loop")
		for _, res := range data.Resources {
			if err := s.AddResource(res); err != nil {
				logrus.WithError(err).Errorf("Failed Adding Resources: %s - %s.", res.Name, res.State)
			}
			logrus.Infof("Successfully Added Resources: %s - %s.", res.Name, res.State)
		}
	}
	return s, nil
}

func NewStorage ( r storage . PersistenceLayer , storage string ) ( * Storage , error ) { s := & Storage { resources : r , } if storage != " " { var data struct { Resources [ ] common . Resource } buf , err := ioutil . ReadFile ( storage ) if err == nil { logrus . Infof ( " " , string ( buf ) ) err = json . Unmarshal ( buf , & data ) if err != nil { return nil , err } } else if ! os . IsNotExist ( err ) { return nil , err } logrus . Info ( " " ) for _ , res := range data . Resources { if err := s . AddResource ( res ) ; err != nil { logrus . WithError ( err ) . Errorf ( " " , res . Name , res . State ) } logrus . Infof ( " " , res . Name , res . State ) } } return s , nil }

// NewStorage instantiates a new Storage with a PersistenceLayer implementation
// If storage string is not empty, it will read resource data from the file

NewStorage instantiates a new Storage with a PersistenceLayer implementation If storage string is not empty it will read resource data from the file


func (s *Storage) AddResource(resource common.Resource) error {
	return s.resources.Add(resource)
}

func ( s * Storage ) AddResource ( resource common . Resource ) error { return s . resources . Add ( resource ) }


func (s *Storage) DeleteResource(name string) error {
	return s.resources.Delete(name)
}

func ( s * Storage ) DeleteResource ( name string ) error { return s . resources . Delete ( name ) }

// DeleteResource deletes a resource if it exists, errors otherwise

DeleteResource deletes a resource if it exists errors otherwise


func (s *Storage) UpdateResource(resource common.Resource) error {
	return s.resources.Update(resource)
}

func ( s * Storage ) UpdateResource ( resource common . Resource ) error { return s . resources . Update ( resource ) }

// UpdateResource updates a resource if it exists, errors otherwise

UpdateResource updates a resource if it exists errors otherwise


func (s *Storage) GetResource(name string) (common.Resource, error) {
	i, err := s.resources.Get(name)
	if err != nil {
		return common.Resource{}, err
	}
	var res common.Resource
	res, err = common.ItemToResource(i)
	if err != nil {
		return common.Resource{}, err
	}
	return res, nil
}

func ( s * Storage ) GetResource ( name string ) ( common . Resource , error ) { i , err := s . resources . Get ( name ) if err != nil { return common . Resource { } , err } var res common . Resource res , err = common . ItemToResource ( i ) if err != nil { return common . Resource { } , err } return res , nil }

// GetResource gets an existing resource, errors otherwise

GetResource gets an existing resource errors otherwise


func (s *Storage) GetResources() ([]common.Resource, error) {
	var resources []common.Resource
	items, err := s.resources.List()
	if err != nil {
		return resources, err
	}
	for _, i := range items {
		var res common.Resource
		res, err = common.ItemToResource(i)
		if err != nil {
			return nil, err
		}
		resources = append(resources, res)
	}
	sort.Stable(common.ResourceByUpdateTime(resources))
	return resources, nil
}

func ( s * Storage ) GetResources ( ) ( [ ] common . Resource , error ) { var resources [ ] common . Resource items , err := s . resources . List ( ) if err != nil { return resources , err } for _ , i := range items { var res common . Resource res , err = common . ItemToResource ( i ) if err != nil { return nil , err } resources = append ( resources , res ) } sort . Stable ( common . ResourceByUpdateTime ( resources ) ) return resources , nil }


func (s *Storage) SyncResources(data []common.Resource) error {
	s.resourcesLock.Lock()
	defer s.resourcesLock.Unlock()

	resources, err := s.GetResources()
	if err != nil {
		logrus.WithError(err).Error("cannot find resources")
		return err
	}

	var finalError error

	// delete non-exist resource
	valid := 0
	for _, res := range resources {
		// If currently busy, yield deletion to later cycles.
		if res.Owner != "" {
			resources[valid] = res
			valid++
			continue
		}
		toDelete := true
		for _, newRes := range data {
			if res.Name == newRes.Name {
				resources[valid] = res
				valid++
				toDelete = false
				break
			}
		}
		if toDelete {
			logrus.Infof("Deleting resource %s", res.Name)
			if err := s.DeleteResource(res.Name); err != nil {
				finalError = multierror.Append(finalError, err)
				logrus.WithError(err).Errorf("unable to delete resource %s", res.Name)
			}
		}
	}
	resources = resources[:valid]

	// add new resource
	for _, p := range data {
		found := false
		for idx := range resources {
			exist := resources[idx]
			if p.Name == exist.Name {
				found = true
				logrus.Infof("Keeping resource %s", p.Name)
				break
			}
		}

		if !found {
			if p.State == "" {
				p.State = common.Free
			}
			logrus.Infof("Adding resource %s", p.Name)
			resources = append(resources, p)
			if err := s.AddResource(p); err != nil {
				logrus.WithError(err).Errorf("unable to add resource %s", p.Name)
				finalError = multierror.Append(finalError, err)
			}
		}
	}
	return finalError
}

// SyncResources will update resources every 10 mins.
// It will append newly added resources to ranch.Resources,
// And try to remove newly deleted resources from ranch.Resources.
// If the newly deleted resource is currently held by a user, the deletion will
// yield to next update cycle.

SyncResources will update resources every 10 mins . It will append newly added resources to ranch . Resources And try to remove newly deleted resources from ranch . Resources . If the newly deleted resource is currently held by a user the deletion will yield to next update cycle .


func ParseConfig(configPath string) ([]common.Resource, error) {
	file, err := ioutil.ReadFile(configPath)
	if err != nil {
		return nil, err
	}

	var data common.BoskosConfig
	err = yaml.Unmarshal(file, &data)
	if err != nil {
		return nil, err
	}

	var resources []common.Resource
	for _, entry := range data.Resources {
		resources = append(resources, common.NewResourcesFromConfig(entry)...)
	}
	return resources, nil
}

func ParseConfig ( configPath string ) ( [ ] common . Resource , error ) { file , err := ioutil . ReadFile ( configPath ) if err != nil { return nil , err } var data common . BoskosConfig err = yaml . Unmarshal ( file , & data ) if err != nil { return nil , err } var resources [ ] common . Resource for _ , entry := range data . Resources { resources = append ( resources , common . NewResourcesFromConfig ( entry ) ... ) } return resources , nil }

// ParseConfig reads in configPath and returns a list of resource objects
// on success.

ParseConfig reads in configPath and returns a list of resource objects on success .


func problemsInFiles(r *git.Repo, files map[string]string) (map[string][]string, error) {
	problems := make(map[string][]string)
	for f := range files {
		src, err := ioutil.ReadFile(filepath.Join(r.Dir, f))
		if err != nil {
			return nil, err
		}
		// This is modeled after the logic from buildifier:
		// https://github.com/bazelbuild/buildtools/blob/8818289/buildifier/buildifier.go#L261
		content, err := build.Parse(f, src)
		if err != nil {
			return nil, fmt.Errorf("parsing as Bazel file %v", err)
		}
		beforeRewrite := build.Format(content)
		var info build.RewriteInfo
		build.Rewrite(content, &info)
		ndata := build.Format(content)
		if !bytes.Equal(src, ndata) && !bytes.Equal(src, beforeRewrite) {
			// TODO(mattmoor): This always seems to be empty?
			problems[f] = uniqProblems(info.Log)
		}
	}
	return problems, nil
}

// problemsInFiles runs buildifier on the files. It returns a map from the file to
// a list of problems with that file.

problemsInFiles runs buildifier on the files . It returns a map from the file to a list of problems with that file .


func NewPodLogArtifact(jobName string, buildID string, sizeLimit int64, ja jobAgent) (*PodLogArtifact, error) {
	if jobName == "" {
		return nil, errInsufficientJobInfo
	}
	if buildID == "" {
		return nil, errInsufficientJobInfo
	}
	if sizeLimit < 0 {
		return nil, errInvalidSizeLimit
	}
	return &PodLogArtifact{
		name:      jobName,
		buildID:   buildID,
		sizeLimit: sizeLimit,
		jobAgent:  ja,
	}, nil
}

func NewPodLogArtifact ( jobName string , buildID string , sizeLimit int64 , ja jobAgent ) ( * PodLogArtifact , error ) { if jobName == " " { return nil , errInsufficientJobInfo } if buildID == " " { return nil , errInsufficientJobInfo } if sizeLimit < 0 { return nil , errInvalidSizeLimit } return & PodLogArtifact { name : jobName , buildID : buildID , sizeLimit : sizeLimit , jobAgent : ja , } , nil }

// NewPodLogArtifact creates a new PodLogArtifact


func (a *PodLogArtifact) CanonicalLink() string {
	q := url.Values{
		"job": []string{a.name},
		"id":  []string{a.buildID},
	}
	u := url.URL{
		Path:     "/log",
		RawQuery: q.Encode(),
	}
	return u.String()
}

func ( a * PodLogArtifact ) CanonicalLink ( ) string { q := url . Values { " " : [ ] string { a . name } , " " : [ ] string { a . buildID } , } u := url . URL { Path : " " , RawQuery : q . Encode ( ) , } return u . String ( ) }

// CanonicalLink returns a link to where pod logs are streamed


func (a *PodLogArtifact) ReadAt(p []byte, off int64) (n int, err error) {
	logs, err := a.jobAgent.GetJobLog(a.name, a.buildID)
	if err != nil {
		return 0, fmt.Errorf("error getting pod log: %v", err)
	}
	r := bytes.NewReader(logs)
	readBytes, err := r.ReadAt(p, off)
	if err == io.EOF {
		return readBytes, io.EOF
	}
	if err != nil {
		return 0, fmt.Errorf("error reading pod logs: %v", err)
	}
	return readBytes, nil
}

func ( a * PodLogArtifact ) ReadAt ( p [ ] byte , off int64 ) ( n int , err error ) { logs , err := a . jobAgent . GetJobLog ( a . name , a . buildID ) if err != nil { return 0 , fmt . Errorf ( " " , err ) } r := bytes . NewReader ( logs ) readBytes , err := r . ReadAt ( p , off ) if err == io . EOF { return readBytes , io . EOF } if err != nil { return 0 , fmt . Errorf ( " " , err ) } return readBytes , nil }

// ReadAt implements reading a range of bytes from the pod logs endpoint


func (a *PodLogArtifact) ReadAll() ([]byte, error) {
	size, err := a.Size()
	if err != nil {
		return nil, fmt.Errorf("error getting pod log size: %v", err)
	}
	if size > a.sizeLimit {
		return nil, lenses.ErrFileTooLarge
	}
	logs, err := a.jobAgent.GetJobLog(a.name, a.buildID)
	if err != nil {
		return nil, fmt.Errorf("error getting pod log: %v", err)
	}
	return logs, nil
}

func ( a * PodLogArtifact ) ReadAll ( ) ( [ ] byte , error ) { size , err := a . Size ( ) if err != nil { return nil , fmt . Errorf ( " " , err ) } if size > a . sizeLimit { return nil , lenses . ErrFileTooLarge } logs , err := a . jobAgent . GetJobLog ( a . name , a . buildID ) if err != nil { return nil , fmt . Errorf ( " " , err ) } return logs , nil }

// ReadAll reads all available pod logs, failing if they are too large

ReadAll reads all available pod logs failing if they are too large


func (a *PodLogArtifact) ReadAtMost(n int64) ([]byte, error) {
	logs, err := a.jobAgent.GetJobLog(a.name, a.buildID)
	if err != nil {
		return nil, fmt.Errorf("error getting pod log: %v", err)
	}
	reader := bytes.NewReader(logs)
	var byteCount int64
	var p []byte
	for byteCount < n {
		b, err := reader.ReadByte()
		if err == io.EOF {
			return p, io.EOF
		}
		if err != nil {
			return nil, fmt.Errorf("error reading pod log: %v", err)
		}
		p = append(p, b)
		byteCount++
	}
	return p, nil
}

func ( a * PodLogArtifact ) ReadAtMost ( n int64 ) ( [ ] byte , error ) { logs , err := a . jobAgent . GetJobLog ( a . name , a . buildID ) if err != nil { return nil , fmt . Errorf ( " " , err ) } reader := bytes . NewReader ( logs ) var byteCount int64 var p [ ] byte for byteCount < n { b , err := reader . ReadByte ( ) if err == io . EOF { return p , io . EOF } if err != nil { return nil , fmt . Errorf ( " " , err ) } p = append ( p , b ) byteCount ++ } return p , nil }


func (a *PodLogArtifact) ReadTail(n int64) ([]byte, error) {
	logs, err := a.jobAgent.GetJobLog(a.name, a.buildID)
	if err != nil {
		return nil, fmt.Errorf("error getting pod log tail: %v", err)
	}
	size := int64(len(logs))
	var off int64
	if n > size {
		off = 0
	} else {
		off = size - n
	}
	p := make([]byte, n)
	readBytes, err := bytes.NewReader(logs).ReadAt(p, off)
	if err != nil && err != io.EOF {
		return nil, fmt.Errorf("error reading pod log tail: %v", err)
	}
	return p[:readBytes], nil
}

func ( a * PodLogArtifact ) ReadTail ( n int64 ) ( [ ] byte , error ) { logs , err := a . jobAgent . GetJobLog ( a . name , a . buildID ) if err != nil { return nil , fmt . Errorf ( " " , err ) } size := int64 ( len ( logs ) ) var off int64 if n > size { off = 0 } else { off = size - n } p := make ( [ ] byte , n ) readBytes , err := bytes . NewReader ( logs ) . ReadAt ( p , off ) if err != nil && err != io . EOF { return nil , fmt . Errorf ( " " , err ) } return p [ : readBytes ] , nil }

// ReadTail reads the last n bytes of the pod log


func newProblems(cs []github.ReviewComment, ps map[string]map[int]lint.Problem) map[string]map[int]lint.Problem {
	// Make a copy, then remove the old elements.
	res := make(map[string]map[int]lint.Problem)
	for f, ls := range ps {
		res[f] = make(map[int]lint.Problem)
		for l, p := range ls {
			res[f][l] = p
		}
	}
	for _, c := range cs {
		if c.Position == nil {
			continue
		}
		if !strings.Contains(c.Body, commentTag) {
			continue
		}
		delete(res[c.Path], *c.Position)
	}
	return res
}

// newProblems compares the list of problems with the list of past comments on
// the PR to decide which are new.

newProblems compares the list of problems with the list of past comments on the PR to decide which are new .


func problemsInFiles(r *git.Repo, files map[string]string) (map[string]map[int]lint.Problem, []github.DraftReviewComment) {
	problems := make(map[string]map[int]lint.Problem)
	var lintErrorComments []github.DraftReviewComment
	l := new(lint.Linter)
	for f, patch := range files {
		problems[f] = make(map[int]lint.Problem)
		src, err := ioutil.ReadFile(filepath.Join(r.Dir, f))
		if err != nil {
			lintErrorComments = append(lintErrorComments, github.DraftReviewComment{
				Path: f,
				Body: fmt.Sprintf("%v", err),
			})
		}
		ps, err := l.Lint(f, src)
		if err != nil {
			// Get error line by parsing the error message
			errLineIndexStart := strings.LastIndex(err.Error(), f) + len(f)
			reNumber := regexp.MustCompile(`:([0-9]+):`)
			matches := reNumber.FindStringSubmatch(err.Error()[errLineIndexStart:])
			newComment := github.DraftReviewComment{
				Path: f,
				Body: err.Error(),
			}
			if len(matches) > 1 {
				errLineString := matches[1]
				errLine, errAtoi := strconv.Atoi(errLineString)
				if errAtoi == nil {
					newComment.Position = errLine
				}
				// Trim error message to after the line and column numbers
				reTrimError := regexp.MustCompile(`(:[0-9]+:[0-9]+: )`)
				matches = reTrimError.FindStringSubmatch(err.Error())
				if len(matches) > 0 {
					newComment.Body = err.Error()[len(matches[0])+errLineIndexStart:]
				}
			}
			lintErrorComments = append(lintErrorComments, newComment)
		}
		al, err := AddedLines(patch)
		if err != nil {
			lintErrorComments = append(lintErrorComments,
				github.DraftReviewComment{
					Path: f,
					Body: fmt.Sprintf("computing added lines in %s: %v", f, err),
				})
		}
		for _, p := range ps {
			if pl, ok := al[p.Position.Line]; ok {
				problems[f][pl] = p
			}
		}
	}
	return problems, lintErrorComments
}

// problemsInFiles runs golint on the files. It returns a map from the file to
// a map from the line in the patch to the problem.

problemsInFiles runs golint on the files . It returns a map from the file to a map from the line in the patch to the problem .


func undoPreset(preset *config.Preset, labels map[string]string, pod *coreapi.PodSpec) {
	// skip presets that do not match the job labels
	for l, v := range preset.Labels {
		if v2, ok := labels[l]; !ok || v2 != v {
			return
		}
	}

	// collect up preset created keys
	removeEnvNames := sets.NewString()
	for _, e1 := range preset.Env {
		removeEnvNames.Insert(e1.Name)
	}
	removeVolumeNames := sets.NewString()
	for _, volume := range preset.Volumes {
		removeVolumeNames.Insert(volume.Name)
	}
	removeVolumeMountNames := sets.NewString()
	for _, volumeMount := range preset.VolumeMounts {
		removeVolumeMountNames.Insert(volumeMount.Name)
	}

	// remove volumes from spec
	filteredVolumes := []coreapi.Volume{}
	for _, volume := range pod.Volumes {
		if !removeVolumeNames.Has(volume.Name) {
			filteredVolumes = append(filteredVolumes, volume)
		}
	}
	pod.Volumes = filteredVolumes

	// remove env and volume mounts from containers
	for i := range pod.Containers {
		filteredEnv := []coreapi.EnvVar{}
		for _, env := range pod.Containers[i].Env {
			if !removeEnvNames.Has(env.Name) {
				filteredEnv = append(filteredEnv, env)
			}
		}
		pod.Containers[i].Env = filteredEnv

		filteredVolumeMounts := []coreapi.VolumeMount{}
		for _, mount := range pod.Containers[i].VolumeMounts {
			if !removeVolumeMountNames.Has(mount.Name) {
				filteredVolumeMounts = append(filteredVolumeMounts, mount)
			}
		}
		pod.Containers[i].VolumeMounts = filteredVolumeMounts
	}
}


func undoPresubmitPresets(presets []config.Preset, presubmit *config.Presubmit) {
	if presubmit.Spec == nil {
		return
	}
	for _, preset := range presets {
		undoPreset(&preset, presubmit.Labels, presubmit.Spec)
	}
}

func undoPresubmitPresets ( presets [ ] config . Preset , presubmit * config . Presubmit ) { if presubmit . Spec == nil { return } for _ , preset := range presets { undoPreset ( & preset , presubmit . Labels , presubmit . Spec ) } }

// undo merged presets from loaded presubmit and its children


func yamlBytesStripNulls(yamlBytes []byte) []byte {
	nullRE := regexp.MustCompile("(?m)[\n]+^[^\n]+: null$")
	return nullRE.ReplaceAll(yamlBytes, []byte{})
}

func yamlBytesStripNulls ( yamlBytes [ ] byte ) [ ] byte { nullRE := regexp . MustCompile ( " \n \n " ) return nullRE . ReplaceAll ( yamlBytes , [ ] byte { } ) }

// these are unnecessary, and make the config larger so we strip them out

these are unnecessary and make the config larger so we strip them out


func monitorDiskAndEvict(
	c *diskcache.Cache,
	interval time.Duration,
	minPercentBlocksFree, evictUntilPercentBlocksFree float64,
) {
	diskRoot := c.DiskRoot()
	// forever check if usage is past thresholds and evict
	ticker := time.NewTicker(interval)
	for ; true; <-ticker.C {
		blocksFree, _, _, err := diskutil.GetDiskUsage(diskRoot)
		if err != nil {
			logrus.WithError(err).Error("Failed to get disk usage!")
			continue
		}
		logger := logrus.WithFields(logrus.Fields{
			"sync-loop":   "MonitorDiskAndEvict",
			"blocks-free": blocksFree,
		})
		logger.Info("tick")
		// if we are past the threshold, start evicting
		if blocksFree < minPercentBlocksFree {
			logger.Warn("Eviction triggered")
			// get all cache entries and sort by lastaccess
			// so we can pop entries until we have evicted enough
			files := c.GetEntries()
			sort.Slice(files, func(i, j int) bool {
				return files[i].LastAccess.Before(files[j].LastAccess)
			})
			// evict until we pass the safe threshold so we don't thrash at the eviction trigger
			for blocksFree < evictUntilPercentBlocksFree {
				if len(files) < 1 {
					logger.Fatal("Failed to find entries to evict!")
				}
				// pop entry and delete
				var entry diskcache.EntryInfo
				entry, files = files[0], files[1:]
				err = c.Delete(c.PathToKey(entry.Path))
				if err != nil {
					logger.WithError(err).Errorf("Error deleting entry at path: %v", entry.Path)
				} else {
					promMetrics.FilesEvicted.Inc()
					promMetrics.LastEvictedAccessAge.Set(time.Now().Sub(entry.LastAccess).Hours())
				}
				// get new disk usage
				blocksFree, _, _, err = diskutil.GetDiskUsage(diskRoot)
				logger = logrus.WithFields(logrus.Fields{
					"sync-loop":   "MonitorDiskAndEvict",
					"blocks-free": blocksFree,
				})
				if err != nil {
					logrus.WithError(err).Error("Failed to get disk usage!")
					continue
				}
			}
			logger.Info("Done evicting")
		}
	}
}

// monitorDiskAndEvict loops monitoring the disk, evicting cache entries
// when the disk passes either minPercentBlocksFree until the disk is above
// evictUntilPercentBlocksFree

monitorDiskAndEvict loops monitoring the disk evicting cache entries when the disk passes either minPercentBlocksFree until the disk is above evictUntilPercentBlocksFree


func (c *orgRepoConfig) difference(c2 *orgRepoConfig) *orgRepoConfig {
	res := &orgRepoConfig{
		orgExceptions: make(map[string]sets.String),
		repos:         sets.NewString().Union(c.repos),
	}
	for org, excepts1 := range c.orgExceptions {
		if excepts2, ok := c2.orgExceptions[org]; ok {
			res.repos.Insert(excepts2.Difference(excepts1).UnsortedList()...)
		} else {
			excepts := sets.NewString().Union(excepts1)
			// Add any applicable repos in repos2 to excepts
			for _, repo := range c2.repos.UnsortedList() {
				if parts := strings.SplitN(repo, "/", 2); len(parts) == 2 && parts[0] == org {
					excepts.Insert(repo)
				}
			}
			res.orgExceptions[org] = excepts
		}
	}

	res.repos = res.repos.Difference(c2.repos)

	for _, repo := range res.repos.UnsortedList() {
		if parts := strings.SplitN(repo, "/", 2); len(parts) == 2 {
			if excepts2, ok := c2.orgExceptions[parts[0]]; ok && !excepts2.Has(repo) {
				res.repos.Delete(repo)
			}
		}
	}
	return res
}

// difference returns a new orgRepoConfig that represents the set difference of
// the repos specified by the receiver and the parameter orgRepoConfigs.

difference returns a new orgRepoConfig that represents the set difference of the repos specified by the receiver and the parameter orgRepoConfigs .


func (c *orgRepoConfig) union(c2 *orgRepoConfig) *orgRepoConfig {
	res := &orgRepoConfig{
		orgExceptions: make(map[string]sets.String),
		repos:         sets.NewString(),
	}

	for org, excepts1 := range c.orgExceptions {
		// keep only items in both blacklists that are not in the
		// explicit repo whitelists for the other configuration;
		// we know from how the orgRepoConfigs are constructed that
		// a org blacklist won't intersect it's own repo whitelist
		pruned := excepts1.Difference(c2.repos)
		if excepts2, ok := c2.orgExceptions[org]; ok {
			res.orgExceptions[org] = pruned.Intersection(excepts2.Difference(c.repos))
		} else {
			res.orgExceptions[org] = pruned
		}
	}

	for org, excepts2 := range c2.orgExceptions {
		// update any blacklists not previously updated
		if _, exists := res.orgExceptions[org]; !exists {
			res.orgExceptions[org] = excepts2.Difference(c.repos)
		}
	}

	// we need to prune out repos in the whitelists which are
	// covered by an org already; we know from above that no
	// org blacklist in the result will contain a repo whitelist
	for _, repo := range c.repos.Union(c2.repos).UnsortedList() {
		parts := strings.SplitN(repo, "/", 2)
		if len(parts) != 2 {
			logrus.Warnf("org/repo %q is formatted incorrectly", repo)
			continue
		}
		if _, exists := res.orgExceptions[parts[0]]; !exists {
			res.repos.Insert(repo)
		}
	}
	return res
}

// union returns a new orgRepoConfig that represents the set union of the
// repos specified by the receiver and the parameter orgRepoConfigs

union returns a new orgRepoConfig that represents the set union of the repos specified by the receiver and the parameter orgRepoConfigs


func clearStaleComments(gc githubClient, log *logrus.Entry, pr *github.PullRequestEvent, prLabels sets.String, comments []github.IssueComment) error {
	// If the PR must follow the process and hasn't yet completed the process, don't remove comments.
	if prMustFollowRelNoteProcess(gc, log, pr, prLabels, false) && !releaseNoteAlreadyAdded(prLabels) {
		return nil
	}
	botName, err := gc.BotName()
	if err != nil {
		return err
	}
	return gc.DeleteStaleComments(
		pr.Repo.Owner.Login,
		pr.Repo.Name,
		pr.Number,
		comments,
		func(c github.IssueComment) bool { // isStale function
			return c.User.Login == botName &&
				(strings.Contains(c.Body, releaseNoteBody) ||
					strings.Contains(c.Body, parentReleaseNoteBody))
		},
	)
}

// clearStaleComments deletes old comments that are no longer applicable.

clearStaleComments deletes old comments that are no longer applicable .


func determineReleaseNoteLabel(body string) string {
	composedReleaseNote := strings.ToLower(strings.TrimSpace(getReleaseNote(body)))

	if composedReleaseNote == "" {
		return ReleaseNoteLabelNeeded
	}
	if noneRe.MatchString(composedReleaseNote) {
		return releaseNoteNone
	}
	if strings.Contains(composedReleaseNote, actionRequiredNote) {
		return releaseNoteActionRequired
	}
	return releaseNote
}

func determineReleaseNoteLabel ( body string ) string { composedReleaseNote := strings . ToLower ( strings . TrimSpace ( getReleaseNote ( body ) ) ) if composedReleaseNote == " " { return ReleaseNoteLabelNeeded } if noneRe . MatchString ( composedReleaseNote ) { return releaseNoteNone } if strings . Contains ( composedReleaseNote , actionRequiredNote ) { return releaseNoteActionRequired } return releaseNote }

// determineReleaseNoteLabel returns the label to be added based on the contents of the 'release-note'
// section of a PR's body text.

determineReleaseNoteLabel returns the label to be added based on the contents of the release - note section of a PR s body text .


func getReleaseNote(body string) string {
	potentialMatch := noteMatcherRE.FindStringSubmatch(body)
	if potentialMatch == nil {
		return ""
	}
	return strings.TrimSpace(potentialMatch[1])
}

func getReleaseNote ( body string ) string { potentialMatch := noteMatcherRE . FindStringSubmatch ( body ) if potentialMatch == nil { return " " } return strings . TrimSpace ( potentialMatch [ 1 ] ) }

// getReleaseNote returns the release note from a PR body
// assumes that the PR body followed the PR template

getReleaseNote returns the release note from a PR body assumes that the PR body followed the PR template


func NewClient(boskosClient boskosClient) *Client {
	return &Client{
		basic:     boskosClient,
		resources: map[string]common.Resource{},
	}
}

func NewClient ( boskosClient boskosClient ) * Client { return & Client { basic : boskosClient , resources : map [ string ] common . Resource { } , } }

// NewClient creates a new client from a boskosClient interface


func (c *Client) Acquire(rtype, state, dest string) (*common.Resource, error) {
	var resourcesToRelease []common.Resource
	releaseOnFailure := func() {
		for _, r := range resourcesToRelease {
			if err := c.basic.ReleaseOne(r.Name, common.Dirty); err != nil {
				logrus.WithError(err).Warningf("failed to release resource %s", r.Name)
			}
		}
	}
	res, err := c.basic.Acquire(rtype, state, dest)
	if err != nil {
		return nil, err
	}
	var leasedResources common.LeasedResources
	if err = res.UserData.Extract(LeasedResources, &leasedResources); err != nil {
		if _, ok := err.(*common.UserDataNotFound); !ok {
			logrus.WithError(err).Errorf("cannot parse %s from User Data", LeasedResources)
			return nil, err
		}
	}
	resourcesToRelease = append(resourcesToRelease, *res)
	resources, err := c.basic.AcquireByState(res.Name, dest, leasedResources)
	if err != nil {
		releaseOnFailure()
		return nil, err
	}
	resourcesToRelease = append(resourcesToRelease, resources...)
	c.updateResource(*res)
	return res, nil
}

func ( c * Client ) Acquire ( rtype , state , dest string ) ( * common . Resource , error ) { var resourcesToRelease [ ] common . Resource releaseOnFailure := func ( ) { for _ , r := range resourcesToRelease { if err := c . basic . ReleaseOne ( r . Name , common . Dirty ) ; err != nil { logrus . WithError ( err ) . Warningf ( " " , r . Name ) } } } res , err := c . basic . Acquire ( rtype , state , dest ) if err != nil { return nil , err } var leasedResources common . LeasedResources if err = res . UserData . Extract ( LeasedResources , & leasedResources ) ; err != nil { if _ , ok := err . ( * common . UserDataNotFound ) ; ! ok { logrus . WithError ( err ) . Errorf ( " " , LeasedResources ) return nil , err } } resourcesToRelease = append ( resourcesToRelease , * res ) resources , err := c . basic . AcquireByState ( res . Name , dest , leasedResources ) if err != nil { releaseOnFailure ( ) return nil , err } resourcesToRelease = append ( resourcesToRelease , resources ... ) c . updateResource ( * res ) return res , nil }

// Acquire gets a resource with associated leased resources


func (c *Client) ReleaseOne(name, dest string) (allErrors error) {
	res, err := c.getResource(name)
	if err != nil {
		allErrors = err
		return
	}
	resourceNames := []string{name}
	var leasedResources common.LeasedResources
	if err := res.UserData.Extract(LeasedResources, &leasedResources); err != nil {
		if _, ok := err.(*common.UserDataNotFound); !ok {
			logrus.WithError(err).Errorf("cannot parse %s from User Data", LeasedResources)
			allErrors = multierror.Append(allErrors, err)
			if err := c.basic.ReleaseOne(name, dest); err != nil {
				logrus.WithError(err).Warningf("failed to release resource %s", name)
				allErrors = multierror.Append(allErrors, err)
			}
			return
		}
	}
	resourceNames = append(resourceNames, leasedResources...)
	for _, n := range resourceNames {
		if err := c.basic.ReleaseOne(n, dest); err != nil {
			logrus.WithError(err).Warningf("failed to release resource %s", n)
			allErrors = multierror.Append(allErrors, err)
		}
	}
	c.deleteResource(name)
	return
}

func ( c * Client ) ReleaseOne ( name , dest string ) ( allErrors error ) { res , err := c . getResource ( name ) if err != nil { allErrors = err return } resourceNames := [ ] string { name } var leasedResources common . LeasedResources if err := res . UserData . Extract ( LeasedResources , & leasedResources ) ; err != nil { if _ , ok := err . ( * common . UserDataNotFound ) ; ! ok { logrus . WithError ( err ) . Errorf ( " " , LeasedResources ) allErrors = multierror . Append ( allErrors , err ) if err := c . basic . ReleaseOne ( name , dest ) ; err != nil { logrus . WithError ( err ) . Warningf ( " " , name ) allErrors = multierror . Append ( allErrors , err ) } return } } resourceNames = append ( resourceNames , leasedResources ... ) for _ , n := range resourceNames { if err := c . basic . ReleaseOne ( n , dest ) ; err != nil { logrus . WithError ( err ) . Warningf ( " " , n ) allErrors = multierror . Append ( allErrors , err ) } } c . deleteResource ( name ) return }

// ReleaseOne will release a resource as well as leased resources associated to it


func (c *Client) UpdateAll(state string) error {
	return c.basic.UpdateAll(state)
}

func ( c * Client ) UpdateAll ( state string ) error { return c . basic . UpdateAll ( state ) }

// UpdateAll updates all the acquired resources with a given state


func GetGitHubClient(token string) *github.Client {
	return github.NewClient(
		oauth2.NewClient(
			oauth2.NoContext,
			oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}),
		),
	)
}

func GetGitHubClient ( token string ) * github . Client { return github . NewClient ( oauth2 . NewClient ( oauth2 . NoContext , oauth2 . StaticTokenSource ( & oauth2 . Token { AccessToken : token } ) , ) , ) }

// GetGitHubClient creates a client for each token


func GetUsername(client *github.Client) (string, error) {
	user, _, err := client.Users.Get(context.Background(), "")
	if err != nil {
		return "", err
	}
	if user.Login == nil {
		return "", errors.New("Users.Get(\"\") returned empty login")
	}

	return *user.Login, nil
}

func GetUsername ( client * github . Client ) ( string , error ) { user , _ , err := client . Users . Get ( context . Background ( ) , " " ) if err != nil { return " " , err } if user . Login == nil { return " " , errors . New ( " \" \" " ) } return * user . Login , nil }

// GetUsername finds the login for each token


func CreateTokenHandler(tokenStream io.Reader, influxdb *InfluxDB) (*TokenHandler, error) {
	token, err := ioutil.ReadAll(tokenStream)
	if err != nil {
		return nil, err
	}
	client := GetGitHubClient(strings.TrimSpace(string(token)))
	login, err := GetUsername(client) // Get user name for token
	if err != nil {
		return nil, err
	}

	return &TokenHandler{
		gClient:  client,
		login:    login,
		influxdb: influxdb,
	}, nil
}

func
CreateTokenHandler
(
tokenStream
io
.
Reader
,
influxdb
*
InfluxDB
)
(
*
TokenHandler
,
error
)
{
token
,
err
:=
ioutil
.
ReadAll
(
tokenStream
)
if
err
!=
{
return
,
err
}
client
:=
GetGitHubClient
(
strings
.
TrimSpace
(
string
(
token
)
)
)
login
,
err
:=
GetUsername
(
client
)
// Get user name for token
if
err
!=
{
return
,
err
}
return
&
TokenHandler
{
gClient
:
client
,
login
:
login
,
influxdb
:
influxdb
,
}
,
}

// CreateTokenHandler parses the token and create a handler


func CreateTokenHandlers(tokenFiles []string, influxdb *InfluxDB) ([]TokenHandler, error) {
	tokens := []TokenHandler{}
	for _, tokenFile := range tokenFiles {
		f, err := os.Open(tokenFile)
		if err != nil {
			return nil, fmt.Errorf("Can't open token-file (%s): %s", tokenFile, err)
		}
		token, err := CreateTokenHandler(f, influxdb)
		if err != nil {
			return nil, fmt.Errorf("Failed to create token (%s): %s", tokenFile, err)
		}
		tokens = append(tokens, *token)
	}
	return tokens, nil
}

func CreateTokenHandlers ( tokenFiles [ ] string , influxdb * InfluxDB ) ( [ ] TokenHandler , error ) { tokens := [ ] TokenHandler { } for _ , tokenFile := range tokenFiles { f , err := os . Open ( tokenFile ) if err != nil { return nil , fmt . Errorf ( " " , tokenFile , err ) } token , err := CreateTokenHandler ( f , influxdb ) if err != nil { return nil , fmt . Errorf ( " " , tokenFile , err ) } tokens = append ( tokens , * token ) } return tokens , nil }

// CreateTokenHandlers goes through the list of token files, and create handlers

CreateTokenHandlers goes through the list of token files and create handlers


func (i *jobIndentifier) String() string {
	return fmt.Sprintf("%s %s/%s#%d", i.job, i.organization, i.repository, i.pullRequest)
}

func ( i * jobIndentifier ) String ( ) string { return fmt . Sprintf ( " " , i . job , i . organization , i . repository , i . pullRequest ) }

// String returns the string representation of a prow job identifier


func TerminateOlderPresubmitJobs(pjc prowClient, log *logrus.Entry, pjs []prowapi.ProwJob,
	cleanup ProwJobResourcesCleanup) error {
	dupes := map[jobIndentifier]int{}
	for i, pj := range pjs {
		if pj.Complete() || pj.Spec.Type != prowapi.PresubmitJob {
			continue
		}

		ji := jobIndentifier{
			job:          pj.Spec.Job,
			organization: pj.Spec.Refs.Org,
			repository:   pj.Spec.Refs.Repo,
			pullRequest:  pj.Spec.Refs.Pulls[0].Number,
		}
		prev, ok := dupes[ji]
		if !ok {
			dupes[ji] = i
			continue
		}
		cancelIndex := i
		if (&pjs[prev].Status.StartTime).Before(&pj.Status.StartTime) {
			cancelIndex = prev
			dupes[ji] = i
		}
		toCancel := pjs[cancelIndex]

		// TODO cancel the prow job before cleaning up its resources and make this system
		// independent.
		// See this discussion for more details:  https://github.com/kubernetes/test-infra/pull/11451#discussion_r263523932
		if err := cleanup(toCancel); err != nil {
			log.WithError(err).WithFields(ProwJobFields(&toCancel)).Warn("Cannot clean up job resources")
		}

		toCancel.SetComplete()
		prevState := toCancel.Status.State
		toCancel.Status.State = prowapi.AbortedState
		log.WithFields(ProwJobFields(&toCancel)).
			WithField("from", prevState).
			WithField("to", toCancel.Status.State).Info("Transitioning states")

		npj, err := pjc.ReplaceProwJob(toCancel.ObjectMeta.Name, toCancel)
		if err != nil {
			return err
		}
		pjs[cancelIndex] = npj
	}

	return nil
}

// TerminateOlderPresubmitJobs aborts all presubmit jobs from the given list that have a newer version. It calls
// the cleanup callback for each job before updating its status as aborted.

TerminateOlderPresubmitJobs aborts all presubmit jobs from the given list that have a newer version . It calls the cleanup callback for each job before updating its status as aborted .


func PushMetrics(component, endpoint string, interval time.Duration) {
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	for {
		select {
		case <-time.Tick(interval):
			if err := push.FromGatherer(component, push.HostnameGroupingKey(), endpoint, prometheus.DefaultGatherer); err != nil {
				logrus.WithField("component", component).WithError(err).Error("Failed to push metrics.")
			}
		case <-sig:
			logrus.WithField("component", component).Infof("Metrics pusher shutting down...")
			return
		}
	}
}

func PushMetrics ( component , endpoint string , interval time . Duration ) { sig := make ( chan os . Signal , 1 ) signal . Notify ( sig , os . Interrupt , syscall . SIGTERM ) for { select { case <- time . Tick ( interval ) : if err := push . FromGatherer ( component , push . HostnameGroupingKey ( ) , endpoint , prometheus . DefaultGatherer ) ; err != nil { logrus . WithField ( " " , component ) . WithError ( err ) . Error ( " " ) } case <- sig : logrus . WithField ( " " , component ) . Infof ( " " ) return } } }

// PushMetrics is meant to run in a goroutine and continuously push
// metrics to the provided endpoint.

PushMetrics is meant to run in a goroutine and continuously push metrics to the provided endpoint .


func RateLimiter(controllerName string) workqueue.RateLimitingInterface {
	rl := workqueue.NewMaxOfRateLimiter(
		workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 120*time.Second),
		&workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(1000), 50000)},
	)
	return workqueue.NewNamedRateLimitingQueue(rl, controllerName)
}

func RateLimiter ( controllerName string ) workqueue . RateLimitingInterface { rl := workqueue . NewMaxOfRateLimiter ( workqueue . NewItemExponentialFailureRateLimiter ( 5 * time . Millisecond , 120 * time . Second ) , & workqueue . BucketRateLimiter { Limiter : rate . NewLimiter ( rate . Limit ( 1000 ) , 50000 ) } , ) return workqueue . NewNamedRateLimitingQueue ( rl , controllerName ) }

// RateLimiter creates a ratelimiting queue for a given prow controller.

RateLimiter creates a ratelimiting queue for a given prow controller .