func (da *DashboardAgent) QueryPullRequests(ctx context.Context, ghc githubClient, query string) ([]PullRequest, error) {
	var prs []PullRequest
	vars := map[string]interface{}{
		"query":        (githubql.String)(query),
		"searchCursor": (*githubql.String)(nil),
	}
	var totalCost int
	var remaining int
	for {
		sq := searchQuery{}
		if err := ghc.Query(ctx, &sq, vars); err != nil {
			return nil, err
		}
		totalCost += int(sq.RateLimit.Cost)
		remaining = int(sq.RateLimit.Remaining)
		for _, n := range sq.Search.Nodes {
			prs = append(prs, n.PullRequest)
		}
		if !sq.Search.PageInfo.HasNextPage {
			break
		}
		vars["searchCursor"] = githubql.NewString(sq.Search.PageInfo.EndCursor)
	}
	da.log.Infof("Search for query \"%s\" cost %d point(s). %d remaining.", query, totalCost, remaining)
	return prs, nil
}

func ( da * DashboardAgent ) QueryPullRequests ( ctx context . Context , ghc githubClient , query string ) ( [ ] PullRequest , error ) { var prs [ ] PullRequest vars := map [ string ] interface { } { " " : ( githubql . String ) ( query ) , " " : ( * githubql . String ) ( nil ) , } var totalCost int var remaining int for { sq := searchQuery { } if err := ghc . Query ( ctx , & sq , vars ) ; err != nil { return nil , err } totalCost += int ( sq . RateLimit . Cost ) remaining = int ( sq . RateLimit . Remaining ) for _ , n := range sq . Search . Nodes { prs = append ( prs , n . PullRequest ) } if ! sq . Search . PageInfo . HasNextPage { break } vars [ " " ] = githubql . NewString ( sq . Search . PageInfo . EndCursor ) } da . log . Infof ( " \" \" " , query , totalCost , remaining ) return prs , nil }

// QueryPullRequests is a query function that returns a list of open pull requests owned by the user whose access token
// is consumed by the github client.

QueryPullRequests is a query function that returns a list of open pull requests owned by the user whose access token is consumed by the github client .


func (da *DashboardAgent) GetHeadContexts(ghc githubClient, pr PullRequest) ([]Context, error) {
	org := string(pr.Repository.Owner.Login)
	repo := string(pr.Repository.Name)
	combined, err := ghc.GetCombinedStatus(org, repo, string(pr.HeadRefOID))
	if err != nil {
		return nil, fmt.Errorf("failed to get the combined status: %v", err)
	}
	contexts := make([]Context, 0, len(combined.Statuses))
	for _, status := range combined.Statuses {
		contexts = append(
			contexts,
			Context{
				Context:     status.Context,
				Description: status.Description,
				State:       strings.ToUpper(status.State),
			},
		)
	}
	return contexts, nil
}

func ( da * DashboardAgent ) GetHeadContexts ( ghc githubClient , pr PullRequest ) ( [ ] Context , error ) { org := string ( pr . Repository . Owner . Login ) repo := string ( pr . Repository . Name ) combined , err := ghc . GetCombinedStatus ( org , repo , string ( pr . HeadRefOID ) ) if err != nil { return nil , fmt . Errorf ( " " , err ) } contexts := make ( [ ] Context , 0 , len ( combined . Statuses ) ) for _ , status := range combined . Statuses { contexts = append ( contexts , Context { Context : status . Context , Description : status . Description , State : strings . ToUpper ( status . State ) , } , ) } return contexts , nil }

// GetHeadContexts returns the status checks' contexts of the head commit of the PR.

GetHeadContexts returns the status checks contexts of the head commit of the PR .


func (da *DashboardAgent) ConstructSearchQuery(login string) string {
	tokens := []string{"is:pr", "state:open", "author:" + login}
	for i := range da.repos {
		tokens = append(tokens, fmt.Sprintf("repo:\"%s\"", da.repos[i]))
	}
	return strings.Join(tokens, " ")
}

func ( da * DashboardAgent ) ConstructSearchQuery ( login string ) string { tokens := [ ] string { " " , " " , " " + login } for i := range da . repos { tokens = append ( tokens , fmt . Sprintf ( " \" \" " , da . repos [ i ] ) ) } return strings . Join ( tokens , " " ) }

// ConstructSearchQuery returns the GitHub search query string for PRs that are open and authored
// by the user passed. The search is scoped to repositories that are configured with either Prow or
// Tide.

ConstructSearchQuery returns the GitHub search query string for PRs that are open and authored by the user passed . The search is scoped to repositories that are configured with either Prow or Tide .


func NewBundledStates(description string) BundledStates {
	return BundledStates{
		description: description,
		states:      map[string]State{},
	}
}

func NewBundledStates ( description string ) BundledStates { return BundledStates { description : description , states : map [ string ] State { } , } }

// NewBundledStates is the constructor for BundledStates


func (b BundledStates) ReceiveEvent(ID string, eventName, label string, t time.Time) bool {
	state, ok := b.states[ID]
	if !ok {
		state = NewState(b.description)
	}
	state, changed := state.ReceiveEvent(eventName, label, t)
	b.states[ID] = state
	return changed
}

func ( b BundledStates ) ReceiveEvent ( ID string , eventName , label string , t time . Time ) bool { state , ok := b . states [ ID ] if ! ok { state = NewState ( b . description ) } state , changed := state . ReceiveEvent ( eventName , label , t ) b . states [ ID ] = state return changed }

// ReceiveEvent is called when something happens on an issue. The state
// for that issue is updated.

ReceiveEvent is called when something happens on an issue . The state for that issue is updated .


func (b BundledStates) ages(t time.Time) map[string]time.Duration {
	ages := map[string]time.Duration{}

	for id, state := range b.states {
		if !state.Active() {
			continue
		}
		ages[id] = state.Age(t)
	}
	return ages
}

func ( b BundledStates ) ages ( t time . Time ) map [ string ] time . Duration { ages := map [ string ] time . Duration { } for id , state := range b . states { if ! state . Active ( ) { continue } ages [ id ] = state . Age ( t ) } return ages }

// ages return the age of each active states


func (b BundledStates) Percentile(t time.Time, percentile int) time.Duration {
	if percentile > 100 || percentile <= 0 {
		panic(fmt.Errorf("percentile %d is out of scope", percentile))
	}

	ages := []time.Duration{}
	for _, age := range b.ages(t) {
		ages = append(ages, age)
	}

	if len(ages) == 0 {
		return 0
	}

	sort.Sort(ByDuration(ages))

	index := int(math.Ceil(float64(percentile)*float64(len(ages))/100) - 1)
	if index >= len(ages) {
		panic(fmt.Errorf("Index is out of range: %d/%d", index, len(ages)))
	}
	return ages[index]
}

func ( b BundledStates ) Percentile ( t time . Time , percentile int ) time . Duration { if percentile > 100 || percentile <= 0 { panic ( fmt . Errorf ( " " , percentile ) ) } ages := [ ] time . Duration { } for _ , age := range b . ages ( t ) { ages = append ( ages , age ) } if len ( ages ) == 0 { return 0 } sort . Sort ( ByDuration ( ages ) ) index := int ( math . Ceil ( float64 ( percentile ) * float64 ( len ( ages ) ) / 100 ) - 1 ) if index >= len ( ages ) { panic ( fmt . Errorf ( " " , index , len ( ages ) ) ) } return ages [ index ] }

// Percentile returns given percentile for age of all active states at time t


func NewMetrics() *Metrics {
	return &Metrics{
		ClientMetrics: &ClientMetrics{
			Requests:       requests,
			RequestRetries: requestRetries,
			RequestLatency: requestLatency,
		},
		ResyncPeriod: resyncPeriod,
	}
}

func NewMetrics ( ) * Metrics { return & Metrics { ClientMetrics : & ClientMetrics { Requests : requests , RequestRetries : requestRetries , RequestLatency : requestLatency , } , ResyncPeriod : resyncPeriod , } }

// NewMetrics creates a new set of metrics for the Jenkins operator.

NewMetrics creates a new set of metrics for the Jenkins operator .


func NewDiskCache(delegate http.RoundTripper, cacheDir string, cacheSizeGB, maxConcurrency int) http.RoundTripper {
	return NewFromCache(delegate, diskcache.NewWithDiskv(
		diskv.New(diskv.Options{
			BasePath:     path.Join(cacheDir, "data"),
			TempDir:      path.Join(cacheDir, "temp"),
			CacheSizeMax: uint64(cacheSizeGB) * uint64(1000000000), // convert G to B
		})),
		maxConcurrency,
	)
}

func
NewDiskCache
(
delegate
http
.
RoundTripper
,
cacheDir
string
,
cacheSizeGB
,
maxConcurrency
int
)
http
.
RoundTripper
{
return
NewFromCache
(
delegate
,
diskcache
.
NewWithDiskv
(
diskv
.
New
(
diskv
.
Options
{
BasePath
:
path
.
Join
(
cacheDir
,
"
"
)
,
TempDir
:
path
.
Join
(
cacheDir
,
"
"
)
,
CacheSizeMax
:
uint64
(
cacheSizeGB
)
*
uint64
(
1000000000
)
,
// convert G to B
}
)
)
,
maxConcurrency
,
)
}

// NewDiskCache creates a GitHub cache RoundTripper that is backed by a disk
// cache.

NewDiskCache creates a GitHub cache RoundTripper that is backed by a disk cache .


func NewMemCache(delegate http.RoundTripper, maxConcurrency int) http.RoundTripper {
	return NewFromCache(delegate, httpcache.NewMemoryCache(), maxConcurrency)
}

func NewMemCache ( delegate http . RoundTripper , maxConcurrency int ) http . RoundTripper { return NewFromCache ( delegate , httpcache . NewMemoryCache ( ) , maxConcurrency ) }

// NewMemCache creates a GitHub cache RoundTripper that is backed by a memory
// cache.

NewMemCache creates a GitHub cache RoundTripper that is backed by a memory cache .


func NewFromCache(delegate http.RoundTripper, cache httpcache.Cache, maxConcurrency int) http.RoundTripper {
	cacheTransport := httpcache.NewTransport(cache)
	cacheTransport.Transport = newThrottlingTransport(maxConcurrency, upstreamTransport{delegate: delegate})
	return &requestCoalescer{
		keys:     make(map[string]*responseWaiter),
		delegate: cacheTransport,
	}
}

func NewFromCache ( delegate http . RoundTripper , cache httpcache . Cache , maxConcurrency int ) http . RoundTripper { cacheTransport := httpcache . NewTransport ( cache ) cacheTransport . Transport = newThrottlingTransport ( maxConcurrency , upstreamTransport { delegate : delegate } ) return & requestCoalescer { keys : make ( map [ string ] * responseWaiter ) , delegate : cacheTransport , } }

// NewFromCache creates a GitHub cache RoundTripper that is backed by the
// specified httpcache.Cache implementation.

NewFromCache creates a GitHub cache RoundTripper that is backed by the specified httpcache . Cache implementation .


func (c *Clientset) ProwV1() prowv1.ProwV1Interface {
	return &fakeprowv1.FakeProwV1{Fake: &c.Fake}
}

func ( c * Clientset ) ProwV1 ( ) prowv1 . ProwV1Interface { return & fakeprowv1 . FakeProwV1 { Fake : & c . Fake } }


func (c *Clientset) Prow() prowv1.ProwV1Interface {
	return &fakeprowv1.FakeProwV1{Fake: &c.Fake}
}

func ( c * Clientset ) Prow ( ) prowv1 . ProwV1Interface { return & fakeprowv1 . FakeProwV1 { Fake : & c . Fake } }


func NewOwners(log *logrus.Entry, filenames []string, r Repo, s int64) Owners {
	return Owners{filenames: filenames, repo: r, seed: s, log: log}
}

func NewOwners ( log * logrus . Entry , filenames [ ] string , r Repo , s int64 ) Owners { return Owners { filenames : filenames , repo : r , seed : s , log : log } }

// NewOwners consturcts a new Owners instance. filenames is the slice of files changed.

NewOwners consturcts a new Owners instance . filenames is the slice of files changed .


func (o Owners) GetApprovers() map[string]sets.String {
	ownersToApprovers := map[string]sets.String{}

	for fn := range o.GetOwnersSet() {
		ownersToApprovers[fn] = o.repo.Approvers(fn)
	}

	return ownersToApprovers
}

func ( o Owners ) GetApprovers ( ) map [ string ] sets . String { ownersToApprovers := map [ string ] sets . String { } for fn := range o . GetOwnersSet ( ) { ownersToApprovers [ fn ] = o . repo . Approvers ( fn ) } return ownersToApprovers }

// GetApprovers returns a map from ownersFiles -> people that are approvers in them

GetApprovers returns a map from ownersFiles - > people that are approvers in them


func (o Owners) GetAllPotentialApprovers() []string {
	approversOnly := []string{}
	for _, approverList := range o.GetLeafApprovers() {
		for approver := range approverList {
			approversOnly = append(approversOnly, approver)
		}
	}
	sort.Strings(approversOnly)
	if len(approversOnly) == 0 {
		o.log.Debug("No potential approvers exist. Does the repo have OWNERS files?")
	}
	return approversOnly
}

func ( o Owners ) GetAllPotentialApprovers ( ) [ ] string { approversOnly := [ ] string { } for _ , approverList := range o . GetLeafApprovers ( ) { for approver := range approverList { approversOnly = append ( approversOnly , approver ) } } sort . Strings ( approversOnly ) if len ( approversOnly ) == 0 { o . log . Debug ( " " ) } return approversOnly }

// GetAllPotentialApprovers returns the people from relevant owners files needed to get the PR approved


func (o Owners) GetReverseMap(approvers map[string]sets.String) map[string]sets.String {
	approverOwnersfiles := map[string]sets.String{}
	for ownersFile, approvers := range approvers {
		for approver := range approvers {
			if _, ok := approverOwnersfiles[approver]; ok {
				approverOwnersfiles[approver].Insert(ownersFile)
			} else {
				approverOwnersfiles[approver] = sets.NewString(ownersFile)
			}
		}
	}
	return approverOwnersfiles
}

func ( o Owners ) GetReverseMap ( approvers map [ string ] sets . String ) map [ string ] sets . String { approverOwnersfiles := map [ string ] sets . String { } for ownersFile , approvers := range approvers { for approver := range approvers { if _ , ok := approverOwnersfiles [ approver ] ; ok { approverOwnersfiles [ approver ] . Insert ( ownersFile ) } else { approverOwnersfiles [ approver ] = sets . NewString ( ownersFile ) } } } return approverOwnersfiles }

// GetReverseMap returns a map from people -> OWNERS files for which they are an approver

GetReverseMap returns a map from people - > OWNERS files for which they are an approver


func (o Owners) temporaryUnapprovedFiles(approvers sets.String) sets.String {
	ap := NewApprovers(o)
	for approver := range approvers {
		ap.AddApprover(approver, "", false)
	}
	return ap.UnapprovedFiles()
}

func ( o Owners ) temporaryUnapprovedFiles ( approvers sets . String ) sets . String { ap := NewApprovers ( o ) for approver := range approvers { ap . AddApprover ( approver , " " , false ) } return ap . UnapprovedFiles ( ) }

// temporaryUnapprovedFiles returns the list of files that wouldn't be
// approved by the given set of approvers.

temporaryUnapprovedFiles returns the list of files that wouldn t be approved by the given set of approvers .


func (o Owners) KeepCoveringApprovers(reverseMap map[string]sets.String, knownApprovers sets.String, potentialApprovers []string) sets.String {
	if len(potentialApprovers) == 0 {
		o.log.Debug("No potential approvers exist to filter for relevance. Does this repo have OWNERS files?")
	}
	keptApprovers := sets.NewString()

	unapproved := o.temporaryUnapprovedFiles(knownApprovers)

	for _, suggestedApprover := range o.GetSuggestedApprovers(reverseMap, potentialApprovers).List() {
		if reverseMap[suggestedApprover].Intersection(unapproved).Len() != 0 {
			keptApprovers.Insert(suggestedApprover)
		}
	}

	return keptApprovers
}

func ( o Owners ) KeepCoveringApprovers ( reverseMap map [ string ] sets . String , knownApprovers sets . String , potentialApprovers [ ] string ) sets . String { if len ( potentialApprovers ) == 0 { o . log . Debug ( " " ) } keptApprovers := sets . NewString ( ) unapproved := o . temporaryUnapprovedFiles ( knownApprovers ) for _ , suggestedApprover := range o . GetSuggestedApprovers ( reverseMap , potentialApprovers ) . List ( ) { if reverseMap [ suggestedApprover ] . Intersection ( unapproved ) . Len ( ) != 0 { keptApprovers . Insert ( suggestedApprover ) } } return keptApprovers }

// KeepCoveringApprovers finds who we should keep as suggested approvers given a pre-selection
// knownApprovers must be a subset of potentialApprovers.

KeepCoveringApprovers finds who we should keep as suggested approvers given a pre - selection knownApprovers must be a subset of potentialApprovers .


func (o Owners) GetSuggestedApprovers(reverseMap map[string]sets.String, potentialApprovers []string) sets.String {
	ap := NewApprovers(o)
	for !ap.RequirementsMet() {
		newApprover := findMostCoveringApprover(potentialApprovers, reverseMap, ap.UnapprovedFiles())
		if newApprover == "" {
			o.log.Warnf("Couldn't find/suggest approvers for each files. Unapproved: %q", ap.UnapprovedFiles().List())
			return ap.GetCurrentApproversSet()
		}
		ap.AddApprover(newApprover, "", false)
	}

	return ap.GetCurrentApproversSet()
}

func ( o Owners ) GetSuggestedApprovers ( reverseMap map [ string ] sets . String , potentialApprovers [ ] string ) sets . String { ap := NewApprovers ( o ) for ! ap . RequirementsMet ( ) { newApprover := findMostCoveringApprover ( potentialApprovers , reverseMap , ap . UnapprovedFiles ( ) ) if newApprover == " " { o . log . Warnf ( " " , ap . UnapprovedFiles ( ) . List ( ) ) return ap . GetCurrentApproversSet ( ) } ap . AddApprover ( newApprover , " " , false ) } return ap . GetCurrentApproversSet ( ) }

// GetSuggestedApprovers solves the exact cover problem, finding an approver capable of
// approving every OWNERS file in the PR

GetSuggestedApprovers solves the exact cover problem finding an approver capable of approving every OWNERS file in the PR


func (o Owners) GetOwnersSet() sets.String {
	owners := sets.NewString()
	for _, fn := range o.filenames {
		owners.Insert(o.repo.FindApproverOwnersForFile(fn))
	}
	o.removeSubdirs(owners)
	return owners
}

func ( o Owners ) GetOwnersSet ( ) sets . String { owners := sets . NewString ( ) for _ , fn := range o . filenames { owners . Insert ( o . repo . FindApproverOwnersForFile ( fn ) ) } o . removeSubdirs ( owners ) return owners }

// GetOwnersSet returns a set containing all the Owners files necessary to get the PR approved


func (o Owners) GetShuffledApprovers() []string {
	approversList := o.GetAllPotentialApprovers()
	order := rand.New(rand.NewSource(o.seed)).Perm(len(approversList))
	people := make([]string, 0, len(approversList))
	for _, i := range order {
		people = append(people, approversList[i])
	}
	return people
}

func ( o Owners ) GetShuffledApprovers ( ) [ ] string { approversList := o . GetAllPotentialApprovers ( ) order := rand . New ( rand . NewSource ( o . seed ) ) . Perm ( len ( approversList ) ) people := make ( [ ] string , 0 , len ( approversList ) ) for _ , i := range order { people = append ( people , approversList [ i ] ) } return people }

// GetShuffledApprovers shuffles the potential approvers so that we don't
// always suggest the same people.

GetShuffledApprovers shuffles the potential approvers so that we don t always suggest the same people .


func (a Approval) String() string {
	return fmt.Sprintf(
		`*<a href="%s" title="%s">%s</a>*`,
		a.Reference,
		a.How,
		a.Login,
	)
}

func
(
a
Approval
)
String
(
)
string
{
return
fmt
.
Sprintf
(
`*<a href="%s" title="%s">%s</a>*`
,
a
.
Reference
,
a
.
How
,
a
.
Login
,
)
}

// String creates a link for the approval. Use `Login` if you just want the name.

String creates a link for the approval . Use Login if you just want the name .


func IntersectSetsCase(one, other sets.String) sets.String {
	lower := sets.NewString()
	for item := range other {
		lower.Insert(strings.ToLower(item))
	}

	intersection := sets.NewString()
	for item := range one {
		if lower.Has(strings.ToLower(item)) {
			intersection.Insert(item)
		}
	}
	return intersection
}

func IntersectSetsCase ( one , other sets . String ) sets . String { lower := sets . NewString ( ) for item := range other { lower . Insert ( strings . ToLower ( item ) ) } intersection := sets . NewString ( ) for item := range one { if lower . Has ( strings . ToLower ( item ) ) { intersection . Insert ( item ) } } return intersection }

// IntersectSetsCase runs the intersection between to sets.String in a
// case-insensitive way. It returns the name with the case of "one".

IntersectSetsCase runs the intersection between to sets . String in a case - insensitive way . It returns the name with the case of one .


func NewApprovers(owners Owners) Approvers {
	return Approvers{
		owners:    owners,
		approvers: map[string]Approval{},
		assignees: sets.NewString(),

		ManuallyApproved: func() bool {
			return false
		},
	}
}

func NewApprovers ( owners Owners ) Approvers { return Approvers { owners : owners , approvers : map [ string ] Approval { } , assignees : sets . NewString ( ) , ManuallyApproved : func ( ) bool { return false } , } }

// NewApprovers create a new "Approvers" with no approval.

NewApprovers create a new Approvers with no approval .


func (ap *Approvers) AddLGTMer(login, reference string, noIssue bool) {
	if ap.shouldNotOverrideApproval(login, noIssue) {
		return
	}
	ap.approvers[strings.ToLower(login)] = Approval{
		Login:     login,
		How:       "LGTM",
		Reference: reference,
		NoIssue:   noIssue,
	}
}

func ( ap * Approvers ) AddLGTMer ( login , reference string , noIssue bool ) { if ap . shouldNotOverrideApproval ( login , noIssue ) { return } ap . approvers [ strings . ToLower ( login ) ] = Approval { Login : login , How : " " , Reference : reference , NoIssue : noIssue , } }


func (ap *Approvers) RemoveApprover(login string) {
	delete(ap.approvers, strings.ToLower(login))
}

func ( ap * Approvers ) RemoveApprover ( login string ) { delete ( ap . approvers , strings . ToLower ( login ) ) }

// RemoveApprover removes an approver from the list.

RemoveApprover removes an approver from the list .


func (ap *Approvers) AddAssignees(logins ...string) {
	for _, login := range logins {
		ap.assignees.Insert(strings.ToLower(login))
	}
}

func ( ap * Approvers ) AddAssignees ( logins ... string ) { for _ , login := range logins { ap . assignees . Insert ( strings . ToLower ( login ) ) } }

// AddAssignees adds assignees to the list


func (ap Approvers) GetCurrentApproversSetCased() sets.String {
	currentApprovers := sets.NewString()

	for _, approval := range ap.approvers {
		currentApprovers.Insert(approval.Login)
	}

	return currentApprovers
}

func ( ap Approvers ) GetCurrentApproversSetCased ( ) sets . String { currentApprovers := sets . NewString ( ) for _ , approval := range ap . approvers { currentApprovers . Insert ( approval . Login ) } return currentApprovers }

// GetCurrentApproversSetCased returns the set of approvers logins with the original cases.

GetCurrentApproversSetCased returns the set of approvers logins with the original cases .


func (ap Approvers) GetFilesApprovers() map[string]sets.String {
	filesApprovers := map[string]sets.String{}
	currentApprovers := ap.GetCurrentApproversSetCased()
	for fn, potentialApprovers := range ap.owners.GetApprovers() {
		// The order of parameter matters here:
		// - currentApprovers is the list of github handles that have approved
		// - potentialApprovers is the list of handles in the OWNER
		// files (lower case).
		//
		// We want to keep the syntax of the github handle
		// rather than the potential mis-cased username found in
		// the OWNERS file, that's why it's the first parameter.
		filesApprovers[fn] = IntersectSetsCase(currentApprovers, potentialApprovers)
	}

	return filesApprovers
}

// GetFilesApprovers returns a map from files -> list of current approvers.

GetFilesApprovers returns a map from files - > list of current approvers .


func (ap Approvers) NoIssueApprovers() map[string]Approval {
	nia := map[string]Approval{}
	reverseMap := ap.owners.GetReverseMap(ap.owners.GetApprovers())

	for login, approver := range ap.approvers {
		if !approver.NoIssue {
			continue
		}

		if len(reverseMap[login]) == 0 {
			continue
		}

		nia[login] = approver
	}

	return nia
}

func ( ap Approvers ) NoIssueApprovers ( ) map [ string ] Approval { nia := map [ string ] Approval { } reverseMap := ap . owners . GetReverseMap ( ap . owners . GetApprovers ( ) ) for login , approver := range ap . approvers { if ! approver . NoIssue { continue } if len ( reverseMap [ login ] ) == 0 { continue } nia [ login ] = approver } return nia }

// NoIssueApprovers returns the list of people who have "no-issue"
// approved the pull-request. They are included in the list iff they can
// approve one of the files.

NoIssueApprovers returns the list of people who have no - issue approved the pull - request . They are included in the list iff they can approve one of the files .


func (ap Approvers) UnapprovedFiles() sets.String {
	unapproved := sets.NewString()
	for fn, approvers := range ap.GetFilesApprovers() {
		if len(approvers) == 0 {
			unapproved.Insert(fn)
		}
	}
	return unapproved
}

func ( ap Approvers ) UnapprovedFiles ( ) sets . String { unapproved := sets . NewString ( ) for fn , approvers := range ap . GetFilesApprovers ( ) { if len ( approvers ) == 0 { unapproved . Insert ( fn ) } } return unapproved }

// UnapprovedFiles returns owners files that still need approval


func (ap Approvers) GetFiles(baseURL *url.URL, branch string) []File {
	allOwnersFiles := []File{}
	filesApprovers := ap.GetFilesApprovers()
	for _, file := range ap.owners.GetOwnersSet().List() {
		if len(filesApprovers[file]) == 0 {
			allOwnersFiles = append(allOwnersFiles, UnapprovedFile{
				baseURL:  baseURL,
				filepath: file,
				branch:   branch,
			})
		} else {
			allOwnersFiles = append(allOwnersFiles, ApprovedFile{
				baseURL:   baseURL,
				filepath:  file,
				approvers: filesApprovers[file],
				branch:    branch,
			})
		}
	}

	return allOwnersFiles
}

func ( ap Approvers ) GetFiles ( baseURL * url . URL , branch string ) [ ] File { allOwnersFiles := [ ] File { } filesApprovers := ap . GetFilesApprovers ( ) for _ , file := range ap . owners . GetOwnersSet ( ) . List ( ) { if len ( filesApprovers [ file ] ) == 0 { allOwnersFiles = append ( allOwnersFiles , UnapprovedFile { baseURL : baseURL , filepath : file , branch : branch , } ) } else { allOwnersFiles = append ( allOwnersFiles , ApprovedFile { baseURL : baseURL , filepath : file , approvers : filesApprovers [ file ] , branch : branch , } ) } } return allOwnersFiles }

// GetFiles returns owners files that still need approval.

GetFiles returns owners files that still need approval .


func (ap Approvers) IsApproved() bool {
	reqsMet := ap.RequirementsMet()
	if !reqsMet && ap.ManuallyApproved() {
		return true
	}
	return reqsMet
}

func ( ap Approvers ) IsApproved ( ) bool { reqsMet := ap . RequirementsMet ( ) if ! reqsMet && ap . ManuallyApproved ( ) { return true } return reqsMet }

// IsApproved returns a bool indicating whether the PR is fully approved.
// If a human manually added the approved label, this returns true, ignoring normal approval rules.

IsApproved returns a bool indicating whether the PR is fully approved . If a human manually added the approved label this returns true ignoring normal approval rules .


func (ap Approvers) ListApprovals() []Approval {
	approvals := []Approval{}

	for _, approver := range ap.GetCurrentApproversSet().List() {
		approvals = append(approvals, ap.approvers[approver])
	}

	return approvals
}

func ( ap Approvers ) ListApprovals ( ) [ ] Approval { approvals := [ ] Approval { } for _ , approver := range ap . GetCurrentApproversSet ( ) . List ( ) { approvals = append ( approvals , ap . approvers [ approver ] ) } return approvals }

// ListApprovals returns the list of approvals


func (ap Approvers) ListNoIssueApprovals() []Approval {
	approvals := []Approval{}

	for _, approver := range ap.GetNoIssueApproversSet().List() {
		approvals = append(approvals, ap.approvers[approver])
	}

	return approvals
}

func ( ap Approvers ) ListNoIssueApprovals ( ) [ ] Approval { approvals := [ ] Approval { } for _ , approver := range ap . GetNoIssueApproversSet ( ) . List ( ) { approvals = append ( approvals , ap . approvers [ approver ] ) } return approvals }

// ListNoIssueApprovals returns the list of "no-issue" approvals

ListNoIssueApprovals returns the list of no - issue approvals


func GenerateTemplate(templ, name string, data interface{}) (string, error) {
	buf := bytes.NewBufferString("")
	if messageTempl, err := template.New(name).Parse(templ); err != nil {
		return "", fmt.Errorf("failed to parse template for %s: %v", name, err)
	} else if err := messageTempl.Execute(buf, data); err != nil {
		return "", fmt.Errorf("failed to execute template for %s: %v", name, err)
	}
	return buf.String(), nil
}

func GenerateTemplate ( templ , name string , data interface { } ) ( string , error ) { buf := bytes . NewBufferString ( " " ) if messageTempl , err := template . New ( name ) . Parse ( templ ) ; err != nil { return " " , fmt . Errorf ( " " , name , err ) } else if err := messageTempl . Execute ( buf , data ) ; err != nil { return " " , fmt . Errorf ( " " , name , err ) } return buf . String ( ) , nil }

// GenerateTemplate takes a template, name and data, and generates
// the corresponding string.

GenerateTemplate takes a template name and data and generates the corresponding string .


func writeTemplate(templatePath string, outputPath string, data interface{}) error {
	// set up template
	funcMap := template.FuncMap{
		"anchor": func(input string) string {
			return strings.Replace(input, ":", " ", -1)
		},
	}
	t, err := template.New(filepath.Base(templatePath)).Funcs(funcMap).ParseFiles(templatePath)
	if err != nil {
		return err
	}

	// ensure output path exists
	if !pathExists(outputPath) {
		_, err = os.Create(outputPath)
		if err != nil {
			return err
		}
	}

	// open file at output path and truncate
	f, err := os.OpenFile(outputPath, os.O_RDWR, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	f.Truncate(0)

	// render template to output path
	err = t.Execute(f, data)
	if err != nil {
		return err
	}

	return nil
}

// Writes the golang text template at templatePath to outputPath using the given data


func (c Configuration) Labels() []Label {
	var labelarrays [][]Label
	labelarrays = append(labelarrays, c.Default.Labels)
	for _, repo := range c.Repos {
		labelarrays = append(labelarrays, repo.Labels)
	}

	labelmap := make(map[string]Label)
	for _, labels := range labelarrays {
		for _, l := range labels {
			name := strings.ToLower(l.Name)
			if _, ok := labelmap[name]; !ok {
				labelmap[name] = l
			}
		}
	}

	var labels []Label
	for _, label := range labelmap {
		labels = append(labels, label)
	}
	sort.Slice(labels, func(i, j int) bool { return labels[i].Name < labels[j].Name })
	return labels
}

func ( c Configuration ) Labels ( ) [ ] Label { var labelarrays [ ] [ ] Label labelarrays = append ( labelarrays , c . Default . Labels ) for _ , repo := range c . Repos { labelarrays = append ( labelarrays , repo . Labels ) } labelmap := make ( map [ string ] Label ) for _ , labels := range labelarrays { for _ , l := range labels { name := strings . ToLower ( l . Name ) if _ , ok := labelmap [ name ] ; ! ok { labelmap [ name ] = l } } } var labels [ ] Label for _ , label := range labelmap { labels = append ( labels , label ) } sort . Slice ( labels , func ( i , j int ) bool { return labels [ i ] . Name < labels [ j ] . Name } ) return labels }

// Labels returns a sorted list of labels unique by name


func LabelsForTarget(labels []Label, target LabelTarget) (filteredLabels []Label) {
	for _, label := range labels {
		if target == label.Target {
			filteredLabels = append(filteredLabels, label)
		}
	}
	// We also sort to make nice tables
	sort.Slice(filteredLabels, func(i, j int) bool { return filteredLabels[i].Name < filteredLabels[j].Name })
	return
}

func
LabelsForTarget
(
labels
[
]
Label
,
target
LabelTarget
)
(
filteredLabels
[
]
Label
)
{
for
_
,
label
:=
range
labels
{
if
target
==
label
.
Target
{
filteredLabels
=
append
(
filteredLabels
,
label
)
}
}
// We also sort to make nice tables
sort
.
Slice
(
filteredLabels
,
func
(
i
,
j
int
)
bool
{
return
filteredLabels
[
i
]
.
Name
<
filteredLabels
[
j
]
.
Name
}
)
return
}

// LabelsForTarget returns labels that have a given target


func LoadConfig(path string, orgs string) (*Configuration, error) {
	if path == "" {
		return nil, errors.New("empty path")
	}
	var c Configuration
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	if err = yaml.Unmarshal(data, &c); err != nil {
		return nil, err
	}
	if err = c.validate(orgs); err != nil { // Ensure no dups
		return nil, err
	}
	return &c, nil
}

func
LoadConfig
(
path
string
,
orgs
string
)
(
*
Configuration
,
error
)
{
if
path
==
"
"
{
return
,
errors
.
New
(
"
"
)
}
var
c
Configuration
data
,
err
:=
ioutil
.
ReadFile
(
path
)
if
err
!=
{
return
,
err
}
if
err
=
yaml
.
Unmarshal
(
data
,
&
c
)
;
err
!=
{
return
,
err
}
if
err
=
c
.
validate
(
orgs
)
;
err
!=
{
// Ensure no dups
return
,
err
}
return
&
c
,
}

// LoadConfig reads the yaml config at path


func loadLabels(gc client, org string, repos []string) (*RepoLabels, error) {
	repoChan := make(chan string, len(repos))
	for _, repo := range repos {
		repoChan <- repo
	}
	close(repoChan)

	wg := sync.WaitGroup{}
	wg.Add(maxConcurrentWorkers)
	labels := make(chan RepoLabels, len(repos))
	errChan := make(chan error, len(repos))
	for i := 0; i < maxConcurrentWorkers; i++ {
		go func(repositories <-chan string) {
			defer wg.Done()
			for repository := range repositories {
				logrus.WithField("org", org).WithField("repo", repository).Info("Listing labels for repo")
				repoLabels, err := gc.GetRepoLabels(org, repository)
				if err != nil {
					logrus.WithField("org", org).WithField("repo", repository).Error("Failed listing labels for repo")
					errChan <- err
				}
				labels <- RepoLabels{repository: repoLabels}
			}
		}(repoChan)
	}

	wg.Wait()
	close(labels)
	close(errChan)

	rl := RepoLabels{}
	for data := range labels {
		for repo, repoLabels := range data {
			rl[repo] = repoLabels
		}
	}

	var overallErr error
	if len(errChan) > 0 {
		var listErrs []error
		for listErr := range errChan {
			listErrs = append(listErrs, listErr)
		}
		overallErr = fmt.Errorf("failed to list labels: %v", listErrs)
	}

	return &rl, overallErr
}

func loadLabels ( gc client , org string , repos [ ] string ) ( * RepoLabels , error ) { repoChan := make ( chan string , len ( repos ) ) for _ , repo := range repos { repoChan <- repo } close ( repoChan ) wg := sync . WaitGroup { } wg . Add ( maxConcurrentWorkers ) labels := make ( chan RepoLabels , len ( repos ) ) errChan := make ( chan error , len ( repos ) ) for i := 0 ; i < maxConcurrentWorkers ; i ++ { go func ( repositories <- chan string ) { defer wg . Done ( ) for repository := range repositories { logrus . WithField ( " " , org ) . WithField ( " " , repository ) . Info ( " " ) repoLabels , err := gc . GetRepoLabels ( org , repository ) if err != nil { logrus . WithField ( " " , org ) . WithField ( " " , repository ) . Error ( " " ) errChan <- err } labels <- RepoLabels { repository : repoLabels } } } ( repoChan ) } wg . Wait ( ) close ( labels ) close ( errChan ) rl := RepoLabels { } for data := range labels { for repo , repoLabels := range data { rl [ repo ] = repoLabels } } var overallErr error if len ( errChan ) > 0 { var listErrs [ ] error for listErr := range errChan { listErrs = append ( listErrs , listErr ) } overallErr = fmt . Errorf ( " " , listErrs ) } return & rl , overallErr }

// loadLabels returns what labels exist in github


func kill(repo string, label Label) Update {
	logrus.WithField("repo", repo).WithField("label", label.Name).Info("kill")
	return Update{Why: "dead", Current: &label, repo: repo}
}

func kill ( repo string , label Label ) Update { logrus . WithField ( " " , repo ) . WithField ( " " , label . Name ) . Info ( " " ) return Update { Why : " " , Current : & label , repo : repo } }


func create(repo string, label Label) Update {
	logrus.WithField("repo", repo).WithField("label", label.Name).Info("create")
	return Update{Why: "missing", Wanted: &label, repo: repo}
}

func create ( repo string , label Label ) Update { logrus . WithField ( " " , repo ) . WithField ( " " , label . Name ) . Info ( " " ) return Update { Why : " " , Wanted : & label , repo : repo } }


func classifyLabels(labels []Label, required, archaic, dead map[string]Label, now time.Time, parent *Label) (map[string]Label, map[string]Label, map[string]Label) {
	newRequired := copyLabelMap(required)
	newArchaic := copyLabelMap(archaic)
	newDead := copyLabelMap(dead)
	for i, l := range labels {
		first := parent
		if first == nil {
			first = &labels[i]
		}
		lower := strings.ToLower(l.Name)
		switch {
		case parent == nil && l.DeleteAfter == nil: // Live label
			newRequired[lower] = l
		case l.DeleteAfter != nil && now.After(*l.DeleteAfter):
			newDead[lower] = l
		case parent != nil:
			l.parent = parent
			newArchaic[lower] = l
		}
		newRequired, newArchaic, newDead = classifyLabels(l.Previously, newRequired, newArchaic, newDead, now, first)
	}
	return newRequired, newArchaic, newDead
}

func
classifyLabels
(
labels
[
]
Label
,
required
,
archaic
,
dead
map
[
string
]
Label
,
now
time
.
Time
,
parent
*
Label
)
(
map
[
string
]
Label
,
map
[
string
]
Label
,
map
[
string
]
Label
)
{
newRequired
:=
copyLabelMap
(
required
)
newArchaic
:=
copyLabelMap
(
archaic
)
newDead
:=
copyLabelMap
(
dead
)
for
i
,
l
:=
range
labels
{
first
:=
parent
if
first
==
{
first
=
&
labels
[
i
]
}
lower
:=
strings
.
ToLower
(
l
.
Name
)
switch
{
case
parent
==
&&
l
.
DeleteAfter
==
:
// Live label
newRequired
[
lower
]
=
l
case
l
.
DeleteAfter
!=
&&
now
.
After
(
*
l
.
DeleteAfter
)
:
newDead
[
lower
]
=
l
case
parent
!=
:
l
.
parent
=
parent
newArchaic
[
lower
]
=
l
}
newRequired
,
newArchaic
,
newDead
=
classifyLabels
(
l
.
Previously
,
newRequired
,
newArchaic
,
newDead
,
now
,
first
)
}
return
newRequired
,
newArchaic
,
newDead
}

// classifyLabels will put labels into the required, archaic, dead maps as appropriate.

classifyLabels will put labels into the required archaic dead maps as appropriate .


func linkify(text string) string {
	// swap space with dash
	link := strings.Replace(text, " ", "-", -1)
	// discard some special characters
	discard, _ := regexp.Compile("[,/]")
	link = discard.ReplaceAllString(link, "")
	// lowercase
	return strings.ToLower(link)
}

func
linkify
(
text
string
)
string
{
// swap space with dash
link
:=
strings
.
Replace
(
text
,
"
"
,
"
"
,
1
)
// discard some special characters
discard
,
_
:=
regexp
.
Compile
(
"
"
)
link
=
discard
.
ReplaceAllString
(
link
,
"
"
)
// lowercase
return
strings
.
ToLower
(
link
)
}

// linkify transforms a string into a markdown anchor link
// I could not find a proper doc, so rules here a mostly empirical

linkify transforms a string into a markdown anchor link I could not find a proper doc so rules here a mostly empirical


func NewCache(diskRoot string) *Cache {
	return &Cache{
		diskRoot: strings.TrimSuffix(diskRoot, string(os.PathListSeparator)),
	}
}

func NewCache ( diskRoot string ) * Cache { return & Cache { diskRoot : strings . TrimSuffix ( diskRoot , string ( os . PathListSeparator ) ) , } }

// NewCache returns a new Cache given the root directory that should be used
// on disk for cache storage

NewCache returns a new Cache given the root directory that should be used on disk for cache storage


func (c *Cache) KeyToPath(key string) string {
	return filepath.Join(c.diskRoot, key)
}

func ( c * Cache ) KeyToPath ( key string ) string { return filepath . Join ( c . diskRoot , key ) }

// KeyToPath converts a cache entry key to a path on disk


func ensureDir(dir string) error {
	if exists(dir) {
		return nil
	}
	return os.MkdirAll(dir, os.FileMode(0744))
}

func ensureDir ( dir string ) error { if exists ( dir ) { return nil } return os . MkdirAll ( dir , os . FileMode ( 0744 ) ) }


func (c *Cache) Put(key string, content io.Reader, contentSHA256 string) error {
	// make sure directory exists
	path := c.KeyToPath(key)
	dir := filepath.Dir(path)
	err := ensureDir(dir)
	if err != nil {
		logrus.WithError(err).Errorf("error ensuring directory '%s' exists", dir)
	}

	// create a temp file to get the content on disk
	temp, err := ioutil.TempFile(dir, "temp-put")
	if err != nil {
		return fmt.Errorf("failed to create cache entry: %v", err)
	}

	// fast path copying when not hashing content,s
	if contentSHA256 == "" {
		_, err = io.Copy(temp, content)
		if err != nil {
			removeTemp(temp.Name())
			return fmt.Errorf("failed to copy into cache entry: %v", err)
		}

	} else {
		hasher := sha256.New()
		_, err = io.Copy(io.MultiWriter(temp, hasher), content)
		if err != nil {
			removeTemp(temp.Name())
			return fmt.Errorf("failed to copy into cache entry: %v", err)
		}
		actualContentSHA256 := hex.EncodeToString(hasher.Sum(nil))
		if actualContentSHA256 != contentSHA256 {
			removeTemp(temp.Name())
			return fmt.Errorf(
				"hashes did not match for '%s', given: '%s' actual: '%s",
				key, contentSHA256, actualContentSHA256)
		}
	}

	// move the content to the key location
	err = temp.Sync()
	if err != nil {
		removeTemp(temp.Name())
		return fmt.Errorf("failed to sync cache entry: %v", err)
	}
	temp.Close()
	err = os.Rename(temp.Name(), path)
	if err != nil {
		removeTemp(temp.Name())
		return fmt.Errorf("failed to insert contents into cache: %v", err)
	}
	return nil
}

// Put copies the content reader until the end into the cache at key
// if contentSHA256 is not "" then the contents will only be stored in the
// cache if the content's hex string SHA256 matches

Put copies the content reader until the end into the cache at key if contentSHA256 is not then the contents will only be stored in the cache if the content s hex string SHA256 matches


func (c *Cache) Get(key string, readHandler ReadHandler) error {
	path := c.KeyToPath(key)
	f, err := os.Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			return readHandler(false, nil)
		}
		return fmt.Errorf("failed to get key: %v", err)
	}
	return readHandler(true, f)
}

func ( c * Cache ) Get ( key string , readHandler ReadHandler ) error { path := c . KeyToPath ( key ) f , err := os . Open ( path ) if err != nil { if os . IsNotExist ( err ) { return readHandler ( false , nil ) } return fmt . Errorf ( " " , err ) } return readHandler ( true , f ) }

// Get provides your readHandler with the contents at key


func (c *Cache) Delete(key string) error {
	return os.Remove(c.KeyToPath(key))
}

func ( c * Cache ) Delete ( key string ) error { return os . Remove ( c . KeyToPath ( key ) ) }


func NewGCSArtifact(ctx context.Context, handle artifactHandle, link string, path string, sizeLimit int64) *GCSArtifact {
	return &GCSArtifact{
		handle:    handle,
		link:      link,
		path:      path,
		sizeLimit: sizeLimit,
		ctx:       ctx,
	}
}

func NewGCSArtifact ( ctx context . Context , handle artifactHandle , link string , path string , sizeLimit int64 ) * GCSArtifact { return & GCSArtifact { handle : handle , link : link , path : path , sizeLimit : sizeLimit , ctx : ctx , } }

// NewGCSArtifact returns a new GCSArtifact with a given handle, canonical link, and path within the job

NewGCSArtifact returns a new GCSArtifact with a given handle canonical link and path within the job


func (a *GCSArtifact) Size() (int64, error) {
	attrs, err := a.handle.Attrs(a.ctx)
	if err != nil {
		return 0, fmt.Errorf("error getting gcs attributes for artifact: %v", err)
	}
	return attrs.Size, nil
}

func ( a * GCSArtifact ) Size ( ) ( int64 , error ) { attrs , err := a . handle . Attrs ( a . ctx ) if err != nil { return 0 , fmt . Errorf ( " " , err ) } return attrs . Size , nil }

// Size returns the size of the artifact in GCS


func (a *GCSArtifact) ReadAll() ([]byte, error) {
	size, err := a.Size()
	if err != nil {
		return nil, fmt.Errorf("error getting artifact size: %v", err)
	}
	if size > a.sizeLimit {
		return nil, lenses.ErrFileTooLarge
	}
	reader, err := a.handle.NewReader(a.ctx)
	if err != nil {
		return nil, fmt.Errorf("error getting artifact reader: %v", err)
	}
	defer reader.Close()
	p, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, fmt.Errorf("error reading all from artifact: %v", err)
	}
	return p, nil
}

func ( a * GCSArtifact ) ReadAll ( ) ( [ ] byte , error ) { size , err := a . Size ( ) if err != nil { return nil , fmt . Errorf ( " " , err ) } if size > a . sizeLimit { return nil , lenses . ErrFileTooLarge } reader , err := a . handle . NewReader ( a . ctx ) if err != nil { return nil , fmt . Errorf ( " " , err ) } defer reader . Close ( ) p , err := ioutil . ReadAll ( reader ) if err != nil { return nil , fmt . Errorf ( " " , err ) } return p , nil }

// ReadAll will either read the entire file or throw an error if file size is too big


func (a *GCSArtifact) ReadTail(n int64) ([]byte, error) {
	gzipped, err := a.gzipped()
	if err != nil {
		return nil, fmt.Errorf("error checking artifact for gzip compression: %v", err)
	}
	if gzipped {
		return nil, lenses.ErrGzipOffsetRead
	}
	size, err := a.Size()
	if err != nil {
		return nil, fmt.Errorf("error getting artifact size: %v", err)
	}
	var offset int64
	if n >= size {
		offset = 0
	} else {
		offset = size - n
	}
	reader, err := a.handle.NewRangeReader(a.ctx, offset, -1)
	defer reader.Close()
	if err != nil && err != io.EOF {
		return nil, fmt.Errorf("error getting artifact reader: %v", err)
	}
	read, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, fmt.Errorf("error reading all from artiact: %v", err)
	}
	return read, nil
}

func ( a * GCSArtifact ) ReadTail ( n int64 ) ( [ ] byte , error ) { gzipped , err := a . gzipped ( ) if err != nil { return nil , fmt . Errorf ( " " , err ) } if gzipped { return nil , lenses . ErrGzipOffsetRead } size , err := a . Size ( ) if err != nil { return nil , fmt . Errorf ( " " , err ) } var offset int64 if n >= size { offset = 0 } else { offset = size - n } reader , err := a . handle . NewRangeReader ( a . ctx , offset , - 1 ) defer reader . Close ( ) if err != nil && err != io . EOF { return nil , fmt . Errorf ( " " , err ) } read , err := ioutil . ReadAll ( reader ) if err != nil { return nil , fmt . Errorf ( " " , err ) } return read , nil }

// ReadTail reads the last n bytes from a file in GCS


func (a *GCSArtifact) gzipped() (bool, error) {
	attrs, err := a.handle.Attrs(a.ctx)
	if err != nil {
		return false, fmt.Errorf("error getting gcs attributes for artifact: %v", err)
	}
	return attrs.ContentEncoding == "gzip", nil
}

func ( a * GCSArtifact ) gzipped ( ) ( bool , error ) { attrs , err := a . handle . Attrs ( a . ctx ) if err != nil { return false , fmt . Errorf ( " " , err ) } return attrs . ContentEncoding == " " , nil }

// gzipped returns whether the file is gzip-encoded in GCS

gzipped returns whether the file is gzip - encoded in GCS


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

	// First search for repo config
	for _, c := range config.Welcome {
		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.Welcome {
		if !strInSlice(org, c.Repos) {
			continue
		}
		return &c
	}

	// Return an empty config, and default to defaultWelcomeMessage
	return &plugins.Welcome{}
}

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

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


func (s *prowJobLister) List(selector labels.Selector) (ret []*v1.ProwJob, err error) {
	err = cache.ListAll(s.indexer, selector, func(m interface{}) {
		ret = append(ret, m.(*v1.ProwJob))
	})
	return ret, err
}

func ( s * prowJobLister ) List ( selector labels . Selector ) ( ret [ ] * v1 . ProwJob , err error ) { err = cache . ListAll ( s . indexer , selector , func ( m interface { } ) { ret = append ( ret , m . ( * v1 . ProwJob ) ) } ) return ret , err }

// List lists all ProwJobs in the indexer.

List lists all ProwJobs in the indexer .


func (s *prowJobLister) ProwJobs(namespace string) ProwJobNamespaceLister {
	return prowJobNamespaceLister{indexer: s.indexer, namespace: namespace}
}

func ( s * prowJobLister ) ProwJobs ( namespace string ) ProwJobNamespaceLister { return prowJobNamespaceLister { indexer : s . indexer , namespace : namespace } }

// ProwJobs returns an object that can list and get ProwJobs.

ProwJobs returns an object that can list and get ProwJobs .


func (s prowJobNamespaceLister) List(selector labels.Selector) (ret []*v1.ProwJob, err error) {
	err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
		ret = append(ret, m.(*v1.ProwJob))
	})
	return ret, err
}

func ( s prowJobNamespaceLister ) List ( selector labels . Selector ) ( ret [ ] * v1 . ProwJob , err error ) { err = cache . ListAllByNamespace ( s . indexer , s . namespace , selector , func ( m interface { } ) { ret = append ( ret , m . ( * v1 . ProwJob ) ) } ) return ret , err }

// List lists all ProwJobs in the indexer for a given namespace.

List lists all ProwJobs in the indexer for a given namespace .


func (br Brancher) RunsAgainstAllBranch() bool {
	return len(br.SkipBranches) == 0 && len(br.Branches) == 0
}

func ( br Brancher ) RunsAgainstAllBranch ( ) bool { return len ( br . SkipBranches ) == 0 && len ( br . Branches ) == 0 }

// RunsAgainstAllBranch returns true if there are both branches and skip_branches are unset


func (br Brancher) Intersects(other Brancher) bool {
	if br.RunsAgainstAllBranch() || other.RunsAgainstAllBranch() {
		return true
	}
	if len(br.Branches) > 0 {
		baseBranches := sets.NewString(br.Branches...)
		if len(other.Branches) > 0 {
			otherBranches := sets.NewString(other.Branches...)
			if baseBranches.Intersection(otherBranches).Len() > 0 {
				return true
			}
			return false
		}

		// Actually test our branches against the other brancher - if there are regex skip lists, simple comparison
		// is insufficient.
		for _, b := range baseBranches.List() {
			if other.ShouldRun(b) {
				return true
			}
		}
		return false
	}
	if len(other.Branches) == 0 {
		// There can only be one Brancher with skip_branches.
		return true
	}
	return other.Intersects(br)
}

// Intersects checks if other Brancher would trigger for the same branch.

Intersects checks if other Brancher would trigger for the same branch .


func (cm RegexpChangeMatcher) ShouldRun(changes ChangedFilesProvider) (determined bool, shouldRun bool, err error) {
	if cm.CouldRun() {
		changeList, err := changes()
		if err != nil {
			return true, false, err
		}
		return true, cm.RunsAgainstChanges(changeList), nil
	}
	return false, false, nil
}

func ( cm RegexpChangeMatcher ) ShouldRun ( changes ChangedFilesProvider ) ( determined bool , shouldRun bool , err error ) { if cm . CouldRun ( ) { changeList , err := changes ( ) if err != nil { return true , false , err } return true , cm . RunsAgainstChanges ( changeList ) , nil } return false , false , nil }

// ShouldRun determines if we can know for certain that the job should run. We can either
// know for certain that the job should or should not run based on the matcher, or we can
// not be able to determine that fact at all.

ShouldRun determines if we can know for certain that the job should run . We can either know for certain that the job should or should not run based on the matcher or we can not be able to determine that fact at all .


func (cm RegexpChangeMatcher) RunsAgainstChanges(changes []string) bool {
	for _, change := range changes {
		if cm.reChanges.MatchString(change) {
			return true
		}
	}
	return false
}

func ( cm RegexpChangeMatcher ) RunsAgainstChanges ( changes [ ] string ) bool { for _ , change := range changes { if cm . reChanges . MatchString ( change ) { return true } } return false }

// RunsAgainstChanges returns true if any of the changed input paths match the run_if_changed regex.

RunsAgainstChanges returns true if any of the changed input paths match the run_if_changed regex .


func (ps Postsubmit) CouldRun(baseRef string) bool {
	return ps.Brancher.ShouldRun(baseRef)
}

func ( ps Postsubmit ) CouldRun ( baseRef string ) bool { return ps . Brancher . ShouldRun ( baseRef ) }

// CouldRun determines if the postsubmit could run against a specific
// base ref

CouldRun determines if the postsubmit could run against a specific base ref


func (ps Postsubmit) ShouldRun(baseRef string, changes ChangedFilesProvider) (bool, error) {
	if !ps.CouldRun(baseRef) {
		return false, nil
	}
	if determined, shouldRun, err := ps.RegexpChangeMatcher.ShouldRun(changes); err != nil {
		return false, err
	} else if determined {
		return shouldRun, nil
	}
	// Postsubmits default to always run
	return true, nil
}

func
(
ps
Postsubmit
)
ShouldRun
(
baseRef
string
,
changes
ChangedFilesProvider
)
(
bool
,
error
)
{
if
!
ps
.
CouldRun
(
baseRef
)
{
return
false
,
}
if
determined
,
shouldRun
,
err
:=
ps
.
RegexpChangeMatcher
.
ShouldRun
(
changes
)
;
err
!=
{
return
false
,
err
}
else
if
determined
{
return
shouldRun
,
}
// Postsubmits default to always run
return
true
,
}

// ShouldRun determines if the postsubmit should run in response to a
// set of changes. This is evaluated lazily, if necessary.

ShouldRun determines if the postsubmit should run in response to a set of changes . This is evaluated lazily if necessary .


func (ps Presubmit) CouldRun(baseRef string) bool {
	return ps.Brancher.ShouldRun(baseRef)
}

func ( ps Presubmit ) CouldRun ( baseRef string ) bool { return ps . Brancher . ShouldRun ( baseRef ) }

// CouldRun determines if the presubmit could run against a specific
// base ref

CouldRun determines if the presubmit could run against a specific base ref


func (ps Presubmit) ShouldRun(baseRef string, changes ChangedFilesProvider, forced, defaults bool) (bool, error) {
	if !ps.CouldRun(baseRef) {
		return false, nil
	}
	if ps.AlwaysRun {
		return true, nil
	}
	if forced {
		return true, nil
	}
	if determined, shouldRun, err := ps.RegexpChangeMatcher.ShouldRun(changes); err != nil {
		return false, err
	} else if determined {
		return shouldRun, nil
	}
	return defaults, nil
}

func ( ps Presubmit ) ShouldRun ( baseRef string , changes ChangedFilesProvider , forced , defaults bool ) ( bool , error ) { if ! ps . CouldRun ( baseRef ) { return false , nil } if ps . AlwaysRun { return true , nil } if forced { return true , nil } if determined , shouldRun , err := ps . RegexpChangeMatcher . ShouldRun ( changes ) ; err != nil { return false , err } else if determined { return shouldRun , nil } return defaults , nil }

// ShouldRun determines if the presubmit should run against a specific
// base ref, or in response to a set of changes. The latter mechanism
// is evaluated lazily, if necessary.

ShouldRun determines if the presubmit should run against a specific base ref or in response to a set of changes . The latter mechanism is evaluated lazily if necessary .


func (c *JobConfig) GetPresubmit(repo, jobName string) *Presubmit {
	presubmits := c.AllPresubmits([]string{repo})
	for i := range presubmits {
		ps := presubmits[i]
		if ps.Name == jobName {
			return &ps
		}
	}
	return nil
}

func ( c * JobConfig ) GetPresubmit ( repo , jobName string ) * Presubmit { presubmits := c . AllPresubmits ( [ ] string { repo } ) for i := range presubmits { ps := presubmits [ i ] if ps . Name == jobName { return & ps } } return nil }

// GetPresubmit returns the presubmit job for the provided repo and job name.

GetPresubmit returns the presubmit job for the provided repo and job name .


func (c *JobConfig) SetPresubmits(jobs map[string][]Presubmit) error {
	nj := map[string][]Presubmit{}
	for k, v := range jobs {
		nj[k] = make([]Presubmit, len(v))
		copy(nj[k], v)
		if err := SetPresubmitRegexes(nj[k]); err != nil {
			return err
		}
	}
	c.Presubmits = nj
	return nil
}

func ( c * JobConfig ) SetPresubmits ( jobs map [ string ] [ ] Presubmit ) error { nj := map [ string ] [ ] Presubmit { } for k , v := range jobs { nj [ k ] = make ( [ ] Presubmit , len ( v ) ) copy ( nj [ k ] , v ) if err := SetPresubmitRegexes ( nj [ k ] ) ; err != nil { return err } } c . Presubmits = nj return nil }

// SetPresubmits updates c.Presubmits to jobs, after compiling and validating their regexes.

SetPresubmits updates c . Presubmits to jobs after compiling and validating their regexes .


func (c *JobConfig) SetPostsubmits(jobs map[string][]Postsubmit) error {
	nj := map[string][]Postsubmit{}
	for k, v := range jobs {
		nj[k] = make([]Postsubmit, len(v))
		copy(nj[k], v)
		if err := SetPostsubmitRegexes(nj[k]); err != nil {
			return err
		}
	}
	c.Postsubmits = nj
	return nil
}

func ( c * JobConfig ) SetPostsubmits ( jobs map [ string ] [ ] Postsubmit ) error { nj := map [ string ] [ ] Postsubmit { } for k , v := range jobs { nj [ k ] = make ( [ ] Postsubmit , len ( v ) ) copy ( nj [ k ] , v ) if err := SetPostsubmitRegexes ( nj [ k ] ) ; err != nil { return err } } c . Postsubmits = nj return nil }

// SetPostsubmits updates c.Postsubmits to jobs, after compiling and validating their regexes.

SetPostsubmits updates c . Postsubmits to jobs after compiling and validating their regexes .


func (c *JobConfig) AllPresubmits(repos []string) []Presubmit {
	var res []Presubmit

	for repo, v := range c.Presubmits {
		if len(repos) == 0 {
			res = append(res, v...)
		} else {
			for _, r := range repos {
				if r == repo {
					res = append(res, v...)
					break
				}
			}
		}
	}

	return res
}

func ( c * JobConfig ) AllPresubmits ( repos [ ] string ) [ ] Presubmit { var res [ ] Presubmit for repo , v := range c . Presubmits { if len ( repos ) == 0 { res = append ( res , v ... ) } else { for _ , r := range repos { if r == repo { res = append ( res , v ... ) break } } } } return res }

// AllPresubmits returns all prow presubmit jobs in repos.
// if repos is empty, return all presubmits.

AllPresubmits returns all prow presubmit jobs in repos . if repos is empty return all presubmits .


func (c *JobConfig) AllPostsubmits(repos []string) []Postsubmit {
	var res []Postsubmit

	for repo, v := range c.Postsubmits {
		if len(repos) == 0 {
			res = append(res, v...)
		} else {
			for _, r := range repos {
				if r == repo {
					res = append(res, v...)
					break
				}
			}
		}
	}

	return res
}

func ( c * JobConfig ) AllPostsubmits ( repos [ ] string ) [ ] Postsubmit { var res [ ] Postsubmit for repo , v := range c . Postsubmits { if len ( repos ) == 0 { res = append ( res , v ... ) } else { for _ , r := range repos { if r == repo { res = append ( res , v ... ) break } } } } return res }

// AllPostsubmits returns all prow postsubmit jobs in repos.
// if repos is empty, return all postsubmits.

AllPostsubmits returns all prow postsubmit jobs in repos . if repos is empty return all postsubmits .


func (c *JobConfig) AllPeriodics() []Periodic {
	var listPeriodic func(ps []Periodic) []Periodic
	listPeriodic = func(ps []Periodic) []Periodic {
		var res []Periodic
		for _, p := range ps {
			res = append(res, p)
		}
		return res
	}

	return listPeriodic(c.Periodics)
}

func ( c * JobConfig ) AllPeriodics ( ) [ ] Periodic { var listPeriodic func ( ps [ ] Periodic ) [ ] Periodic listPeriodic = func ( ps [ ] Periodic ) [ ] Periodic { var res [ ] Periodic for _ , p := range ps { res = append ( res , p ) } return res } return listPeriodic ( c . Periodics ) }

// AllPeriodics returns all prow periodic jobs.

AllPeriodics returns all prow periodic jobs .


func ClearCompiledRegexes(presubmits []Presubmit) {
	for i := range presubmits {
		presubmits[i].re = nil
		presubmits[i].Brancher.re = nil
		presubmits[i].Brancher.reSkip = nil
		presubmits[i].RegexpChangeMatcher.reChanges = nil
	}
}

func ClearCompiledRegexes ( presubmits [ ] Presubmit ) { for i := range presubmits { presubmits [ i ] . re = nil presubmits [ i ] . Brancher . re = nil presubmits [ i ] . Brancher . reSkip = nil presubmits [ i ] . RegexpChangeMatcher . reChanges = nil } }

// ClearCompiledRegexes removes compiled regexes from the presubmits,
// useful for testing when deep equality is needed between presubmits

ClearCompiledRegexes removes compiled regexes from the presubmits useful for testing when deep equality is needed between presubmits


func (s *SimpleConfig) Empty() bool {
	return len(s.Approvers) == 0 && len(s.Reviewers) == 0 && len(s.RequiredReviewers) == 0 && len(s.Labels) == 0
}

func ( s * SimpleConfig ) Empty ( ) bool { return len ( s . Approvers ) == 0 && len ( s . Reviewers ) == 0 && len ( s . RequiredReviewers ) == 0 && len ( s . Labels ) == 0 }

// Empty checks if a SimpleConfig could be considered empty


func NewClient(
	gc *git.Client,
	ghc *github.Client,
	mdYAMLEnabled func(org, repo string) bool,
	skipCollaborators func(org, repo string) bool,
	ownersDirBlacklist func() prowConf.OwnersDirBlacklist,
) *Client {
	return &Client{
		git:    gc,
		ghc:    ghc,
		logger: logrus.WithField("client", "repoowners"),
		cache:  make(map[string]cacheEntry),

		mdYAMLEnabled:      mdYAMLEnabled,
		skipCollaborators:  skipCollaborators,
		ownersDirBlacklist: ownersDirBlacklist,
	}
}

func NewClient ( gc * git . Client , ghc * github . Client , mdYAMLEnabled func ( org , repo string ) bool , skipCollaborators func ( org , repo string ) bool , ownersDirBlacklist func ( ) prowConf . OwnersDirBlacklist , ) * Client { return & Client { git : gc , ghc : ghc , logger : logrus . WithField ( " " , " " ) , cache : make ( map [ string ] cacheEntry ) , mdYAMLEnabled : mdYAMLEnabled , skipCollaborators : skipCollaborators , ownersDirBlacklist : ownersDirBlacklist , } }

// NewClient is the constructor for Client


func (a RepoAliases) ExpandAlias(alias string) sets.String {
	if a == nil {
		return nil
	}
	return a[github.NormLogin(alias)]
}

func ( a RepoAliases ) ExpandAlias ( alias string ) sets . String { if a == nil { return nil } return a [ github . NormLogin ( alias ) ] }

// ExpandAlias returns members of an alias


func (a RepoAliases) ExpandAliases(logins sets.String) sets.String {
	if a == nil {
		return logins
	}
	// Make logins a copy of the original set to avoid modifying the original.
	logins = logins.Union(nil)
	for _, login := range logins.List() {
		if expanded := a.ExpandAlias(login); len(expanded) > 0 {
			logins.Delete(login)
			logins = logins.Union(expanded)
		}
	}
	return logins
}

// ExpandAliases returns members of multiple aliases, duplicates are pruned

ExpandAliases returns members of multiple aliases duplicates are pruned


func ParseFullConfig(b []byte) (FullConfig, error) {
	full := new(FullConfig)
	err := yaml.Unmarshal(b, full)
	return *full, err
}

func ParseFullConfig ( b [ ] byte ) ( FullConfig , error ) { full := new ( FullConfig ) err := yaml . Unmarshal ( b , full ) return * full , err }

// ParseFullConfig will unmarshal OWNERS file's content into a FullConfig
// Returns an error if the content cannot be unmarshalled

ParseFullConfig will unmarshal OWNERS file s content into a FullConfig Returns an error if the content cannot be unmarshalled


func ParseSimpleConfig(b []byte) (SimpleConfig, error) {
	simple := new(SimpleConfig)
	err := yaml.Unmarshal(b, simple)
	return *simple, err
}

func ParseSimpleConfig ( b [ ] byte ) ( SimpleConfig , error ) { simple := new ( SimpleConfig ) err := yaml . Unmarshal ( b , simple ) return * simple , err }

// ParseSimpleConfig will unmarshal an OWNERS file's content into a SimpleConfig
// Returns an error if the content cannot be unmarshalled

ParseSimpleConfig will unmarshal an OWNERS file s content into a SimpleConfig Returns an error if the content cannot be unmarshalled


func decodeOwnersMdConfig(path string, config *SimpleConfig) error {
	fileBytes, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	// Parse the yaml header from the top of the file.  Will return an empty string if regex does not match.
	meta := mdStructuredHeaderRegex.FindString(string(fileBytes))

	// Unmarshal the yaml header into the config
	return yaml.Unmarshal([]byte(meta), &config)
}

// decodeOwnersMdConfig will parse the yaml header if it exists and unmarshal it into a singleOwnersConfig.
// If no yaml header is found, do nothing
// Returns an error if the file cannot be read or the yaml header is found but cannot be unmarshalled.

decodeOwnersMdConfig will parse the yaml header if it exists and unmarshal it into a singleOwnersConfig . If no yaml header is found do nothing Returns an error if the file cannot be read or the yaml header is found but cannot be unmarshalled .


func findOwnersForFile(log *logrus.Entry, path string, ownerMap map[string]map[*regexp.Regexp]sets.String) string {
	d := path

	for ; d != baseDirConvention; d = canonicalize(filepath.Dir(d)) {
		relative, err := filepath.Rel(d, path)
		if err != nil {
			log.WithError(err).WithField("path", path).Errorf("Unable to find relative path between %q and path.", d)
			return ""
		}
		for re, n := range ownerMap[d] {
			if re != nil && !re.MatchString(relative) {
				continue
			}
			if len(n) != 0 {
				return d
			}
		}
	}
	return ""
}

func findOwnersForFile ( log * logrus . Entry , path string , ownerMap map [ string ] map [ * regexp . Regexp ] sets . String ) string { d := path for ; d != baseDirConvention ; d = canonicalize ( filepath . Dir ( d ) ) { relative , err := filepath . Rel ( d , path ) if err != nil { log . WithError ( err ) . WithField ( " " , path ) . Errorf ( " " , d ) return " " } for re , n := range ownerMap [ d ] { if re != nil && ! re . MatchString ( relative ) { continue } if len ( n ) != 0 { return d } } } return " " }

// findOwnersForFile returns the OWNERS file path furthest down the tree for a specified file
// using ownerMap to check for entries

findOwnersForFile returns the OWNERS file path furthest down the tree for a specified file using ownerMap to check for entries


func (o *RepoOwners) FindApproverOwnersForFile(path string) string {
	return findOwnersForFile(o.log, path, o.approvers)
}

func ( o * RepoOwners ) FindApproverOwnersForFile ( path string ) string { return findOwnersForFile ( o . log , path , o . approvers ) }

// FindApproverOwnersForFile returns the OWNERS file path furthest down the tree for a specified file
// that contains an approvers section

FindApproverOwnersForFile returns the OWNERS file path furthest down the tree for a specified file that contains an approvers section


func (o *RepoOwners) FindReviewersOwnersForFile(path string) string {
	return findOwnersForFile(o.log, path, o.reviewers)
}

func ( o * RepoOwners ) FindReviewersOwnersForFile ( path string ) string { return findOwnersForFile ( o . log , path , o . reviewers ) }

// FindReviewersOwnersForFile returns the OWNERS file path furthest down the tree for a specified file
// that contains a reviewers section

FindReviewersOwnersForFile returns the OWNERS file path furthest down the tree for a specified file that contains a reviewers section


func (o *RepoOwners) FindLabelsForFile(path string) sets.String {
	return o.entriesForFile(path, o.labels, false)
}

func ( o * RepoOwners ) FindLabelsForFile ( path string ) sets . String { return o . entriesForFile ( path , o . labels , false ) }

// FindLabelsForFile returns a set of labels which should be applied to PRs
// modifying files under the given path.

FindLabelsForFile returns a set of labels which should be applied to PRs modifying files under the given path .


func (o *RepoOwners) IsNoParentOwners(path string) bool {
	return o.options[path].NoParentOwners
}

func ( o * RepoOwners ) IsNoParentOwners ( path string ) bool { return o . options [ path ] . NoParentOwners }

// IsNoParentOwners checks if an OWNERS file path refers to an OWNERS file with NoParentOwners enabled.

IsNoParentOwners checks if an OWNERS file path refers to an OWNERS file with NoParentOwners enabled .


func (c *Coverage) Ratio() float32 {
	if c.NumAllStmts == 0 {
		return 1
	}
	return float32(c.NumCoveredStmts) / float32(c.NumAllStmts)
}

func ( c * Coverage ) Ratio ( ) float32 { if c . NumAllStmts == 0 { return 1 } return float32 ( c . NumCoveredStmts ) / float32 ( c . NumAllStmts ) }

// Ratio returns the percentage of statements that are covered


func (pe *PeriodicProwJobEvent) FromPayload(data []byte) error {
	if err := json.Unmarshal(data, pe); err != nil {
		return err
	}
	return nil
}

func ( pe * PeriodicProwJobEvent ) FromPayload ( data [ ] byte ) error { if err := json . Unmarshal ( data , pe ) ; err != nil { return err } return nil }

// FromPayload set the PeriodicProwJobEvent from the PubSub message payload.

FromPayload set the PeriodicProwJobEvent from the PubSub message payload .


func (pe *PeriodicProwJobEvent) ToMessage() (*pubsub.Message, error) {
	data, err := json.Marshal(pe)
	if err != nil {
		return nil, err
	}
	message := pubsub.Message{
		Data: data,
		Attributes: map[string]string{
			prowEventType: periodicProwJobEvent,
		},
	}
	return &message, nil
}

func ( pe * PeriodicProwJobEvent ) ToMessage ( ) ( * pubsub . Message , error ) { data , err := json . Marshal ( pe ) if err != nil { return nil , err } message := pubsub . Message { Data : data , Attributes : map [ string ] string { prowEventType : periodicProwJobEvent , } , } return & message , nil }

// ToMessage generates a PubSub Message from a PeriodicProwJobEvent.

ToMessage generates a PubSub Message from a PeriodicProwJobEvent .


func (p *Privacy) UnmarshalText(text []byte) error {
	v := Privacy(text)
	if _, ok := privacySettings[v]; !ok {
		return fmt.Errorf("bad privacy setting: %s", v)
	}
	*p = v
	return nil
}

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

// UnmarshalText returns an error if text != secret or closed

UnmarshalText returns an error if text ! = secret or closed


func compileApplicableBlockades(org, repo string, log *logrus.Entry, blockades []plugins.Blockade) []blockade {
	if len(blockades) == 0 {
		return nil
	}

	orgRepo := fmt.Sprintf("%s/%s", org, repo)
	var compiled []blockade
	for _, raw := range blockades {
		// Only consider blockades that apply to this repo.
		if !stringInSlice(org, raw.Repos) && !stringInSlice(orgRepo, raw.Repos) {
			continue
		}
		b := blockade{}
		for _, str := range raw.BlockRegexps {
			if reg, err := regexp.Compile(str); err != nil {
				log.WithError(err).Errorf("Failed to compile the blockade regexp '%s'.", str)
			} else {
				b.blockRegexps = append(b.blockRegexps, reg)
			}
		}
		if len(b.blockRegexps) == 0 {
			continue
		}
		if raw.Explanation == "" {
			b.explanation = "Files are protected"
		} else {
			b.explanation = raw.Explanation
		}
		for _, str := range raw.ExceptionRegexps {
			if reg, err := regexp.Compile(str); err != nil {
				log.WithError(err).Errorf("Failed to compile the blockade regexp '%s'.", str)
			} else {
				b.exceptionRegexps = append(b.exceptionRegexps, reg)
			}
		}
		compiled = append(compiled, b)
	}
	return compiled
}

// compileApplicableBlockades filters the specified blockades and compiles those that apply to the repo.

compileApplicableBlockades filters the specified blockades and compiles those that apply to the repo .


func calculateBlocks(changes []github.PullRequestChange, blockades []blockade) summary {
	sum := make(summary)
	for _, change := range changes {
		for _, b := range blockades {
			if b.isBlocked(change.Filename) {
				sum[b.explanation] = append(sum[b.explanation], change)
			}
		}
	}
	return sum
}

func calculateBlocks ( changes [ ] github . PullRequestChange , blockades [ ] blockade ) summary { sum := make ( summary ) for _ , change := range changes { for _ , b := range blockades { if b . isBlocked ( change . Filename ) { sum [ b . explanation ] = append ( sum [ b . explanation ] , change ) } } } return sum }

// calculateBlocks determines if a PR should be blocked and returns the summary describing the block.

calculateBlocks determines if a PR should be blocked and returns the summary describing the block .


func MergeMultipleProfiles(profiles [][]*cover.Profile) ([]*cover.Profile, error) {
	if len(profiles) < 1 {
		return nil, errors.New("can't merge zero profiles")
	}
	result := profiles[0]
	for _, profile := range profiles[1:] {
		var err error
		if result, err = MergeProfiles(result, profile); err != nil {
			return nil, err
		}
	}
	return result, nil
}

func MergeMultipleProfiles ( profiles [ ] [ ] * cover . Profile ) ( [ ] * cover . Profile , error ) { if len ( profiles ) < 1 { return nil , errors . New ( " " ) } result := profiles [ 0 ] for _ , profile := range profiles [ 1 : ] { var err error if result , err = MergeProfiles ( result , profile ) ; err != nil { return nil , err } } return result , nil }

// MergeMultipleProfiles merges more than two profiles together.
// MergeMultipleProfiles is equivalent to calling MergeProfiles on pairs of profiles
// until only one profile remains.

MergeMultipleProfiles merges more than two profiles together . MergeMultipleProfiles is equivalent to calling MergeProfiles on pairs of profiles until only one profile remains .


func (o *Options) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&o.ProcessLog, "process-log", "", "path to the log where stdout and stderr are streamed for the process we execute")
	fs.StringVar(&o.MarkerFile, "marker-file", "", "file we write the return code of the process we execute once it has finished running")
	fs.StringVar(&o.MetadataFile, "metadata-file", "", "path to the metadata file generated from the job")
}

func ( o * Options ) AddFlags ( fs * flag . FlagSet ) { fs . StringVar ( & o . ProcessLog , " " , " " , " " ) fs . StringVar ( & o . MarkerFile , " " , " " , " " ) fs . StringVar ( & o . MetadataFile , " " , " " , " " ) }

// AddFlags adds flags to the FlagSet that populate
// the wrapper options struct provided.

AddFlags adds flags to the FlagSet that populate the wrapper options struct provided .


func (c *Controller) processNextItem() bool {
	key, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(key)

	workItem := key.(item)

	prowJob, err := c.prowJobClient.GetProwJob(workItem.prowJobId)
	if err != nil {
		c.handleErr(err, workItem)
		return true
	}
	spec := downwardapi.NewJobSpec(prowJob.Spec, prowJob.Status.BuildID, prowJob.Name)

	result := c.client.Pods(workItem.namespace).GetLogs(workItem.podName, &api.PodLogOptions{Container: workItem.containerName}).Do()
	if err := result.Error(); err != nil {
		c.handleErr(err, workItem)
		return true
	}

	// error is checked above
	log, _ := result.Raw()
	var target string
	if workItem.podName == workItem.prowJobId {
		target = path.Join(ContainerLogDir, fmt.Sprintf("%s.txt", workItem.containerName))
	} else {
		target = path.Join(ContainerLogDir, workItem.podName, fmt.Sprintf("%s.txt", workItem.containerName))
	}
	data := gcs.DataUpload(bytes.NewReader(log))
	if err := c.gcsConfig.Run(&spec, map[string]gcs.UploadFunc{target: data}); err != nil {
		c.handleErr(err, workItem)
		return true
	}
	c.queue.Forget(key)
	return true
}

func
(
c
*
Controller
)
processNextItem
(
)
bool
{
key
,
quit
:=
c
.
queue
.
Get
(
)
if
quit
{
return
false
}
defer
c
.
queue
.
Done
(
key
)
workItem
:=
key
.
(
item
)
prowJob
,
err
:=
c
.
prowJobClient
.
GetProwJob
(
workItem
.
prowJobId
)
if
err
!=
{
c
.
handleErr
(
err
,
workItem
)
return
true
}
spec
:=
downwardapi
.
NewJobSpec
(
prowJob
.
Spec
,
prowJob
.
Status
.
BuildID
,
prowJob
.
Name
)
result
:=
c
.
client
.
Pods
(
workItem
.
namespace
)
.
GetLogs
(
workItem
.
podName
,
&
api
.
PodLogOptions
{
Container
:
workItem
.
containerName
}
)
.
Do
(
)
if
err
:=
result
.
Error
(
)
;
err
!=
{
c
.
handleErr
(
err
,
workItem
)
return
true
}
// error is checked above
log
,
_
:=
result
.
Raw
(
)
var
target
string
if
workItem
.
podName
==
workItem
.
prowJobId
{
target
=
path
.
Join
(
ContainerLogDir
,
fmt
.
Sprintf
(
"
"
,
workItem
.
containerName
)
)
}
else
{
target
=
path
.
Join
(
ContainerLogDir
,
workItem
.
podName
,
fmt
.
Sprintf
(
"
"
,
workItem
.
containerName
)
)
}
data
:=
gcs
.
DataUpload
(
bytes
.
NewReader
(
log
)
)
if
err
:=
c
.
gcsConfig
.
Run
(
&
spec
,
map
[
string
]
gcs
.
UploadFunc
{
target
:
data
}
)
;
err
!=
{
c
.
handleErr
(
err
,
workItem
)
return
true
}
c
.
queue
.
Forget
(
key
)
return
true
}

// processNextItem attempts to upload container logs to GCS


func (c *Controller) handleErr(err error, key item) {
	if c.queue.NumRequeues(key) < 5 {
		glog.Infof("Error uploading logs for container %v in pod %v: %v", key.containerName, key.podName, err)
		c.queue.AddRateLimited(key)
		return
	}

	c.queue.Forget(key)
	glog.Infof("Giving up on upload of logs for container %v in pod %v: %v", key.containerName, key.podName, err)
}

func ( c * Controller ) handleErr ( err error , key item ) { if c . queue . NumRequeues ( key ) < 5 { glog . Infof ( " " , key . containerName , key . podName , err ) c . queue . AddRateLimited ( key ) return } c . queue . Forget ( key ) glog . Infof ( " " , key . containerName , key . podName , err ) }

// handleErr checks if an error happened and makes sure we will retry later.

handleErr checks if an error happened and makes sure we will retry later .


func AggregateFilter(filters []Filter) Filter {
	return func(presubmit config.Presubmit) (bool, bool, bool) {
		for _, filter := range filters {
			if shouldRun, forced, defaults := filter(presubmit); shouldRun {
				return shouldRun, forced, defaults
			}
		}
		return false, false, false
	}
}

func AggregateFilter ( filters [ ] Filter ) Filter { return func ( presubmit config . Presubmit ) ( bool , bool , bool ) { for _ , filter := range filters { if shouldRun , forced , defaults := filter ( presubmit ) ; shouldRun { return shouldRun , forced , defaults } } return false , false , false } }

// AggregateFilter builds a filter that evaluates the child filters in order
// and returns the first match

AggregateFilter builds a filter that evaluates the child filters in order and returns the first match


func FilterPresubmits(filter Filter, changes config.ChangedFilesProvider, branch string, presubmits []config.Presubmit, logger *logrus.Entry) ([]config.Presubmit, []config.Presubmit, error) {

	var toTrigger []config.Presubmit
	var toSkip []config.Presubmit
	for _, presubmit := range presubmits {
		matches, forced, defaults := filter(presubmit)
		if !matches {
			continue
		}
		shouldRun, err := presubmit.ShouldRun(branch, changes, forced, defaults)
		if err != nil {
			return nil, nil, err
		}
		if shouldRun {
			toTrigger = append(toTrigger, presubmit)
		} else {
			toSkip = append(toSkip, presubmit)
		}
	}

	logger.WithFields(logrus.Fields{"to-trigger": toTrigger, "to-skip": toSkip}).Debugf("Filtered %d jobs, found %d to trigger and %d to skip.", len(presubmits), len(toTrigger), len(toSkip))
	return toTrigger, toSkip, nil
}

func FilterPresubmits ( filter Filter , changes config . ChangedFilesProvider , branch string , presubmits [ ] config . Presubmit , logger * logrus . Entry ) ( [ ] config . Presubmit , [ ] config . Presubmit , error ) { var toTrigger [ ] config . Presubmit var toSkip [ ] config . Presubmit for _ , presubmit := range presubmits { matches , forced , defaults := filter ( presubmit ) if ! matches { continue } shouldRun , err := presubmit . ShouldRun ( branch , changes , forced , defaults ) if err != nil { return nil , nil , err } if shouldRun { toTrigger = append ( toTrigger , presubmit ) } else { toSkip = append ( toSkip , presubmit ) } } logger . WithFields ( logrus . Fields { " " : toTrigger , " " : toSkip } ) . Debugf ( " " , len ( presubmits ) , len ( toTrigger ) , len ( toSkip ) ) return toTrigger , toSkip , nil }

// FilterPresubmits determines which presubmits should run and which should be skipped
// by evaluating the user-provided filter.

FilterPresubmits determines which presubmits should run and which should be skipped by evaluating the user - provided filter .


func MakeCommand() *cobra.Command {
	flags := &flags{}
	cmd := &cobra.Command{
		Use:   "filter [file]",
		Short: "Filters a Go coverage file.",
		Long:  `Filters a Go coverage file, removing entries that do not match the given flags.`,
		Run: func(cmd *cobra.Command, args []string) {
			run(flags, cmd, args)
		},
	}
	cmd.Flags().StringVarP(&flags.OutputFile, "output", "o", "-", "output file")
	cmd.Flags().StringSliceVar(&flags.IncludePaths, "include-path", nil, "If specified at least once, only files with paths matching one of these regexes are included.")
	cmd.Flags().StringSliceVar(&flags.ExcludePaths, "exclude-path", nil, "Files with paths matching one of these regexes are excluded. Can be used repeatedly.")
	return cmd
}

// MakeCommand returns a `filter` command.


func (t *EventTimeHeap) Push(x interface{}) {
	*t = append(*t, x.(sql.IssueEvent))
}

func ( t * EventTimeHeap ) Push ( x interface { } ) { * t = append ( * t , x . ( sql . IssueEvent ) ) }


func (t *EventTimeHeap) Pop() interface{} {
	old := *t
	n := len(old)
	x := old[n-1]
	*t = old[0 : n-1]
	return x
}

func ( t * EventTimeHeap ) Pop ( ) interface { } { old := * t n := len ( old ) x := old [ n - 1 ] * t = old [ 0 : n - 1 ] return x }


func NewFakeOpenPluginWrapper(plugin Plugin) *FakeOpenPluginWrapper {
	return &FakeOpenPluginWrapper{
		plugin:      plugin,
		alreadyOpen: map[string]bool{},
	}
}

func NewFakeOpenPluginWrapper ( plugin Plugin ) * FakeOpenPluginWrapper { return & FakeOpenPluginWrapper { plugin : plugin , alreadyOpen : map [ string ] bool { } , } }

// NewFakeOpenPluginWrapper is the constructor for FakeOpenPluginWrapper


func (o *FakeOpenPluginWrapper) ReceiveIssue(issue sql.Issue) []Point {
	if _, ok := o.alreadyOpen[issue.ID]; !ok {
		// Create/Add fake "opened" events
		heap.Push(&o.openEvents, sql.IssueEvent{
			Event:          "opened",
			IssueID:        issue.ID,
			Actor:          &issue.User,
			EventCreatedAt: issue.IssueCreatedAt,
		})
		o.alreadyOpen[issue.ID] = true
	}

	return o.plugin.ReceiveIssue(issue)
}

func
(
o
*
FakeOpenPluginWrapper
)
ReceiveIssue
(
issue
sql
.
Issue
)
[
]
Point
{
if
_
,
ok
:=
o
.
alreadyOpen
[
issue
.
ID
]
;
!
ok
{
// Create/Add fake "opened" events
heap
.
Push
(
&
o
.
openEvents
,
sql
.
IssueEvent
{
Event
:
"
"
,
IssueID
:
issue
.
ID
,
Actor
:
&
issue
.
User
,
EventCreatedAt
:
issue
.
IssueCreatedAt
,
}
)
o
.
alreadyOpen
[
issue
.
ID
]
=
true
}
return
o
.
plugin
.
ReceiveIssue
(
issue
)
}

// ReceiveIssue creates a fake "opened" event

ReceiveIssue creates a fake opened event


func (o *Options) Validate() error {
	if o.SrcRoot == "" {
		return errors.New("no source root specified")
	}

	if o.Log == "" {
		return errors.New("no log file specified")
	}

	if len(o.GitRefs) == 0 {
		return errors.New("no refs specified to clone")
	}

	seen := map[string]sets.String{}
	for _, ref := range o.GitRefs {
		if _, seenOrg := seen[ref.Org]; seenOrg {
			if seen[ref.Org].Has(ref.Repo) {
				return errors.New("sync config for %s/%s provided more than once")
			}
			seen[ref.Org].Insert(ref.Repo)
		} else {
			seen[ref.Org] = sets.NewString(ref.Repo)
		}
	}

	return nil
}

func ( o * Options ) Validate ( ) error { if o . SrcRoot == " " { return errors . New ( " " ) } if o . Log == " " { return errors . New ( " " ) } if len ( o . GitRefs ) == 0 { return errors . New ( " " ) } seen := map [ string ] sets . String { } for _ , ref := range o . GitRefs { if _ , seenOrg := seen [ ref . Org ] ; seenOrg { if seen [ ref . Org ] . Has ( ref . Repo ) { return errors . New ( " " ) } seen [ ref . Org ] . Insert ( ref . Repo ) } else { seen [ ref . Org ] = sets . NewString ( ref . Repo ) } } return nil }

// Validate ensures that the configuration options are valid


func (o *Options) Complete(args []string) {
	o.GitRefs = o.refs.gitRefs
	o.KeyFiles = o.keys.data

	for _, ref := range o.GitRefs {
		alias, err := o.clonePath.Execute(OrgRepo{Org: ref.Org, Repo: ref.Repo})
		if err != nil {
			panic(err)
		}
		ref.PathAlias = alias

		alias, err = o.cloneURI.Execute(OrgRepo{Org: ref.Org, Repo: ref.Repo})
		if err != nil {
			panic(err)
		}
		ref.CloneURI = alias
	}
}

func ( o * Options ) Complete ( args [ ] string ) { o . GitRefs = o . refs . gitRefs o . KeyFiles = o . keys . data for _ , ref := range o . GitRefs { alias , err := o . clonePath . Execute ( OrgRepo { Org : ref . Org , Repo : ref . Repo } ) if err != nil { panic ( err ) } ref . PathAlias = alias alias , err = o . cloneURI . Execute ( OrgRepo { Org : ref . Org , Repo : ref . Repo } ) if err != nil { panic ( err ) } ref . CloneURI = alias } }

// Complete internalizes command line arguments


func (a *orgRepoFormat) Set(value string) error {
	templ, err := template.New("format").Parse(value)
	if err != nil {
		return err
	}
	a.raw = value
	a.format = templ
	return nil
}

func ( a * orgRepoFormat ) Set ( value string ) error { templ , err := template . New ( " " ) . Parse ( value ) if err != nil { return err } a . raw = value a . format = templ return nil }

// Set parses out overrides from user input


func ensure(binary, install string) error {
	if _, err := exec.LookPath(binary); err != nil {
		return fmt.Errorf("%s: %s", binary, install)
	}
	return nil
}

func ensure ( binary , install string ) error { if _ , err := exec . LookPath ( binary ) ; err != nil { return fmt . Errorf ( " " , binary , install ) } return nil }

// ensure will ensure binary is on path or return an error with install message.

ensure will ensure binary is on path or return an error with install message .


func output(args ...string) (string, error) {
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	b, err := cmd.Output()
	return strings.TrimSpace(string(b)), err
}

func output ( args ... string ) ( string , error ) { cmd := exec . Command ( args [ 0 ] , args [ 1 : ] ... ) cmd . Stderr = os . Stderr cmd . Stdin = os . Stdin b , err := cmd . Output ( ) return strings . TrimSpace ( string ( b ) ) , err }

// output returns the trimmed output of running args, or an err on non-zero exit.

output returns the trimmed output of running args or an err on non - zero exit .


func projects(max int) ([]string, error) {
	out, err := output("gcloud", "projects", "list", fmt.Sprintf("--limit=%d", max), "--format=value(project_id)")
	if err != nil {
		return nil, err
	}
	return strings.Split(out, "\n"), nil
}

func projects ( max int ) ( [ ] string , error ) { out , err := output ( " " , " " , " " , fmt . Sprintf ( " " , max ) , " " ) if err != nil { return nil , err } return strings . Split ( out , " \n " ) , nil }

// projects returns the list of accessible gcp projects


func selectProject(choice string) (string, error) {
	fmt.Print("Getting active GCP account...")
	who, err := currentAccount()
	if err != nil {
		logrus.Warn("Run gcloud auth login to initialize gcloud")
		return "", err
	}
	fmt.Println(who)

	var projs []string

	if choice == "" {
		fmt.Printf("Projects available to %s:", who)
		fmt.Println()
		const max = 20
		projs, err = projects(max)
		for _, proj := range projs {
			fmt.Println("  *", proj)
		}
		if err != nil {
			return "", fmt.Errorf("list projects: %v", err)
		}
		if len(projs) == 0 {
			fmt.Println("Create a project at https://console.cloud.google.com/")
			return "", errors.New("no projects")
		}
		if len(projs) == max {
			fmt.Println("  ... Wow, that is a lot of projects!")
			fmt.Println("Type the name of any project, including ones not in this truncated list")
		}

		def, err := currentProject()
		if err != nil {
			return "", fmt.Errorf("get current project: %v", err)
		}
		fmt.Printf("Select project [%s]: ", def)
		fmt.Scanln(&choice)

		// use default project
		if choice == "" {
			return def, nil
		}
	}

	// is this a project from the list?
	for _, p := range projs {
		if p == choice {
			return choice, nil
		}
	}

	fmt.Printf("Ensuring %s has access to %s...", who, choice)
	fmt.Println()

	// no, make sure user has access to it
	if err = exec.Command("gcloud", "projects", "describe", choice).Run(); err != nil {
		return "", fmt.Errorf("%s cannot describe project: %v", who, err)
	}

	return choice, nil
}

func
selectProject
(
choice
string
)
(
string
,
error
)
{
fmt
.
Print
(
"
"
)
who
,
err
:=
currentAccount
(
)
if
err
!=
{
logrus
.
Warn
(
"
"
)
return
"
"
,
err
}
fmt
.
Println
(
who
)
var
projs
[
]
string
if
choice
==
"
"
{
fmt
.
Printf
(
"
"
,
who
)
fmt
.
Println
(
)
const
max
=
20
projs
,
err
=
projects
(
max
)
for
_
,
proj
:=
range
projs
{
fmt
.
Println
(
"
"
,
proj
)
}
if
err
!=
{
return
"
"
,
fmt
.
Errorf
(
"
"
,
err
)
}
if
len
(
projs
)
==
0
{
fmt
.
Println
(
"
"
)
return
"
"
,
errors
.
New
(
"
"
)
}
if
len
(
projs
)
==
max
{
fmt
.
Println
(
"
"
)
fmt
.
Println
(
"
"
)
}
def
,
err
:=
currentProject
(
)
if
err
!=
{
return
"
"
,
fmt
.
Errorf
(
"
"
,
err
)
}
fmt
.
Printf
(
"
"
,
def
)
fmt
.
Scanln
(
&
choice
)
// use default project
if
choice
==
"
"
{
return
def
,
}
}
// is this a project from the list?
for
_
,
p
:=
range
projs
{
if
p
==
choice
{
return
choice
,
}
}
fmt
.
Printf
(
"
"
,
who
,
choice
)
fmt
.
Println
(
)
// no, make sure user has access to it
if
err
=
exec
.
Command
(
"
"
,
"
"
,
"
"
,
choice
)
.
Run
(
)
;
err
!=
{
return
"
"
,
fmt
.
Errorf
(
"
"
,
who
,
err
)
}
return
choice
,
}

// selectProject returns the user-selected project, defaulting to the current gcloud one.

selectProject returns the user - selected project defaulting to the current gcloud one .


func createCluster(proj, choice string) (*cluster, error) {
	const def = "prow"
	if choice == "" {
		fmt.Printf("Cluster name [%s]: ", def)
		fmt.Scanln(&choice)
		if choice == "" {
			choice = def
		}
	}

	cmd := exec.Command("gcloud", "container", "clusters", "create", choice)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return nil, fmt.Errorf("create cluster: %v", err)
	}

	out, err := output("gcloud", "container", "clusters", "describe", choice, "--format=value(name,zone)")
	if err != nil {
		return nil, fmt.Errorf("describe cluster: %v", err)
	}
	parts := strings.Split(out, "\t")
	if len(parts) != 2 {
		return nil, fmt.Errorf("bad describe cluster output: %s", out)
	}

	return &cluster{name: parts[0], zone: parts[1], project: proj}, nil
}

func createCluster ( proj , choice string ) ( * cluster , error ) { const def = " " if choice == " " { fmt . Printf ( " " , def ) fmt . Scanln ( & choice ) if choice == " " { choice = def } } cmd := exec . Command ( " " , " " , " " , " " , choice ) cmd . Stdin = os . Stdin cmd . Stdout = os . Stdout cmd . Stderr = os . Stderr if err := cmd . Run ( ) ; err != nil { return nil , fmt . Errorf ( " " , err ) } out , err := output ( " " , " " , " " , " " , choice , " " ) if err != nil { return nil , fmt . Errorf ( " " , err ) } parts := strings . Split ( out , " \t " ) if len ( parts ) != 2 { return nil , fmt . Errorf ( " " , out ) } return & cluster { name : parts [ 0 ] , zone : parts [ 1 ] , project : proj } , nil }

// createCluster causes gcloud to create a cluster in project, returning the context name

createCluster causes gcloud to create a cluster in project returning the context name


func createContext(co contextOptions) (string, error) {
	proj, err := selectProject(co.project)
	if err != nil {
		logrus.Info("Run gcloud auth login to initialize gcloud")
		return "", fmt.Errorf("get current project: %v", err)
	}

	fmt.Printf("Existing GKE clusters in %s:", proj)
	fmt.Println()
	clusters, err := currentClusters(proj)
	if err != nil {
		return "", fmt.Errorf("list %s clusters: %v", proj, err)
	}
	for name := range clusters {
		fmt.Println("  *", name)
	}
	if len(clusters) == 0 {
		fmt.Println("  No clusters")
	}
	var choice string
	create := co.create
	reuse := co.reuse
	switch {
	case create != "" && reuse != "":
		return "", errors.New("Cannot use both --create and --reuse")
	case create != "":
		fmt.Println("Creating new " + create + " cluster...")
		choice = "new"
	case reuse != "":
		fmt.Println("Reusing existing " + reuse + " cluster...")
		choice = reuse
	default:
		fmt.Print("Get credentials for existing cluster or [create new]: ")
		fmt.Scanln(&choice)
	}

	if choice == "" || choice == "new" {
		cluster, err := createCluster(proj, create)
		if err != nil {
			return "", fmt.Errorf("create cluster in %s: %v", proj, err)
		}
		return cluster.context(), nil
	}

	cluster, ok := clusters[choice]
	if !ok {
		return "", fmt.Errorf("cluster not found: %s", choice)
	}
	cmd := exec.Command("gcloud", "container", "clusters", "get-credentials", cluster.name, "--project="+cluster.project, "--zone="+cluster.zone)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("get credentials: %v", err)
	}
	return cluster.context(), nil
}

func createContext ( co contextOptions ) ( string , error ) { proj , err := selectProject ( co . project ) if err != nil { logrus . Info ( " " ) return " " , fmt . Errorf ( " " , err ) } fmt . Printf ( " " , proj ) fmt . Println ( ) clusters , err := currentClusters ( proj ) if err != nil { return " " , fmt . Errorf ( " " , proj , err ) } for name := range clusters { fmt . Println ( " " , name ) } if len ( clusters ) == 0 { fmt . Println ( " " ) } var choice string create := co . create reuse := co . reuse switch { case create != " " && reuse != " " : return " " , errors . New ( " " ) case create != " " : fmt . Println ( " " + create + " " ) choice = " " case reuse != " " : fmt . Println ( " " + reuse + " " ) choice = reuse default : fmt . Print ( " " ) fmt . Scanln ( & choice ) } if choice == " " || choice == " " { cluster , err := createCluster ( proj , create ) if err != nil { return " " , fmt . Errorf ( " " , proj , err ) } return cluster . context ( ) , nil } cluster , ok := clusters [ choice ] if ! ok { return " " , fmt . Errorf ( " " , choice ) } cmd := exec . Command ( " " , " " , " " , " " , cluster . name , " " + cluster . project , " " + cluster . zone ) cmd . Stdin = os . Stdin cmd . Stdout = os . Stdout cmd . Stderr = os . Stderr if err := cmd . Run ( ) ; err != nil { return " " , fmt . Errorf ( " " , err ) } return cluster . context ( ) , nil }

// createContext has the user create a context.

createContext has the user create a context .


func contextConfig() (clientcmd.ClientConfigLoader, *clientcmdapi.Config, error) {
	if err := ensureKubectl(); err != nil {
		fmt.Println("Prow's tackler requires kubectl, please install:")
		fmt.Println("  *", err)
		if gerr := ensureGcloud(); gerr != nil {
			fmt.Println("  *", gerr)
		}
		return nil, nil, errors.New("missing kubectl")
	}

	l := clientcmd.NewDefaultClientConfigLoadingRules()
	c, err := l.Load()
	return l, c, err
}

func contextConfig ( ) ( clientcmd . ClientConfigLoader , * clientcmdapi . Config , error ) { if err := ensureKubectl ( ) ; err != nil { fmt . Println ( " " ) fmt . Println ( " " , err ) if gerr := ensureGcloud ( ) ; gerr != nil { fmt . Println ( " " , gerr ) } return nil , nil , errors . New ( " " ) } l := clientcmd . NewDefaultClientConfigLoadingRules ( ) c , err := l . Load ( ) return l , c , err }

// contextConfig returns the loader and config, which can create a clientconfig.

contextConfig returns the loader and config which can create a clientconfig .


func selectContext(co contextOptions) (string, error) {
	fmt.Println("Existing kubernetes contexts:")
	// get cluster context
	_, cfg, err := contextConfig()
	if err != nil {
		logrus.WithError(err).Fatal("Failed to load ~/.kube/config from any obvious location")
	}
	// list contexts and ask to user to choose a context
	options := map[int]string{}

	var ctxs []string
	for ctx := range cfg.Contexts {
		ctxs = append(ctxs, ctx)
	}
	sort.Strings(ctxs)
	for idx, ctx := range ctxs {
		options[idx] = ctx
		if ctx == cfg.CurrentContext {
			fmt.Printf("* %d: %s (current)", idx, ctx)
		} else {
			fmt.Printf("  %d: %s", idx, ctx)
		}
		fmt.Println()
	}
	fmt.Println()
	choice := co.context
	switch {
	case choice != "":
		fmt.Println("Reuse " + choice + " context...")
	case co.create != "" || co.reuse != "":
		choice = "create"
		fmt.Println("Create new context...")
	default:
		fmt.Print("Choose context or [create new]: ")
		fmt.Scanln(&choice)
	}

	if choice == "create" || choice == "" || choice == "create new" || choice == "new" {
		ctx, err := createContext(co)
		if err != nil {
			return "", fmt.Errorf("create context: %v", err)
		}
		return ctx, nil
	}

	if _, ok := cfg.Contexts[choice]; ok {
		return choice, nil
	}

	idx, err := strconv.Atoi(choice)
	if err != nil {
		return "", fmt.Errorf("invalid context: %q", choice)
	}

	if ctx, ok := options[idx]; ok {
		return ctx, nil
	}

	return "", fmt.Errorf("invalid index: %d", idx)
}

// selectContext allows the user to choose a context
// This may involve creating a cluster

selectContext allows the user to choose a context This may involve creating a cluster


func applyCreate(ctx string, args ...string) error {
	create := exec.Command("kubectl", append([]string{"--dry-run=true", "--output=yaml", "create"}, args...)...)
	create.Stderr = os.Stderr
	obj, err := create.StdoutPipe()
	if err != nil {
		return fmt.Errorf("rolebinding pipe: %v", err)
	}

	if err := create.Start(); err != nil {
		return fmt.Errorf("start create: %v", err)
	}
	if err := apply(ctx, obj); err != nil {
		return fmt.Errorf("apply: %v", err)
	}
	if err := create.Wait(); err != nil {
		return fmt.Errorf("create: %v", err)
	}
	return nil
}

func applyCreate ( ctx string , args ... string ) error { create := exec . Command ( " " , append ( [ ] string { " " , " " , " " } , args ... ) ... ) create . Stderr = os . Stderr obj , err := create . StdoutPipe ( ) if err != nil { return fmt . Errorf ( " " , err ) } if err := create . Start ( ) ; err != nil { return fmt . Errorf ( " " , err ) } if err := apply ( ctx , obj ) ; err != nil { return fmt . Errorf ( " " , err ) } if err := create . Wait ( ) ; err != nil { return fmt . Errorf ( " " , err ) } return nil }

// applyCreate will dry-run create and then pipe this to kubectl apply.
//
// If we use the create verb it will fail if the secret already exists.
// And kubectl will reject the apply verb with a secret.

applyCreate will dry - run create and then pipe this to kubectl apply . If we use the create verb it will fail if the secret already exists . And kubectl will reject the apply verb with a secret .