mirror of
https://github.com/actions/actions-runner-controller.git
synced 2025-12-10 11:41:27 +00:00
This will work on GHES but GitHub Enterprise Cloud due to excessive GitHub API calls required. More work is needed, like adding a cache layer to the GitHub client, to make it usable on GitHub Enterprise Cloud. Fixes additional cases from https://github.com/actions-runner-controller/actions-runner-controller/pull/1012 If GitHub auth is provided in the webhooks controller then runner groups with custom visibility are supported. Otherwise, all runner groups will be assumed to be visible to all repositories `getScaleUpTargetWithFunction()` will check if there is an HRA available with the following flow: 1. Search for **repository** HRAs - if so it ends here 2. Get available HRAs in k8s 3. Compute visible runner groups a. If GitHub auth is provided - get all the runner groups that are visible to the repository of the incoming webhook using GitHub API calls. b. If GitHub auth is not provided - assume all runner groups are visible to all repositories 4. Search for **default organization** runners (a.k.a runners from organization's visible default runner group) with matching labels 5. Search for **default enterprise** runners (a.k.a runners from enterprise's visible default runner group) with matching labels 6. Search for **custom organization runner groups** with matching labels 7. Search for **custom enterprise runner groups** with matching labels Co-authored-by: Yusuke Kuoka <ykuoka@gmail.com>
181 lines
3.8 KiB
Go
181 lines
3.8 KiB
Go
package simulator
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/google/go-github/v39/github"
|
|
)
|
|
|
|
type RunnerGroupScope int
|
|
|
|
const (
|
|
Organization RunnerGroupScope = iota
|
|
Enterprise
|
|
)
|
|
|
|
func (s RunnerGroupScope) String() string {
|
|
switch s {
|
|
case Organization:
|
|
return "Organization"
|
|
case Enterprise:
|
|
return "Enterprise"
|
|
default:
|
|
panic(fmt.Sprintf("unimplemented RunnerGroupScope: %v", int(s)))
|
|
}
|
|
}
|
|
|
|
type RunnerGroupKind int
|
|
|
|
const (
|
|
Default RunnerGroupKind = iota
|
|
Custom
|
|
)
|
|
|
|
func (s RunnerGroupKind) String() string {
|
|
switch s {
|
|
case Default:
|
|
return "Default"
|
|
case Custom:
|
|
return "Custom"
|
|
default:
|
|
panic(fmt.Sprintf("unimplemented RunnerGroupKind: %v", int(s)))
|
|
}
|
|
}
|
|
|
|
func NewRunnerGroupFromGitHub(g *github.RunnerGroup) RunnerGroup {
|
|
var name string
|
|
if !g.GetDefault() {
|
|
name = g.GetName()
|
|
}
|
|
|
|
var scope RunnerGroupScope
|
|
|
|
if g.GetInherited() {
|
|
scope = Enterprise
|
|
} else {
|
|
scope = Organization
|
|
}
|
|
|
|
return newRunnerGroup(scope, name)
|
|
}
|
|
|
|
func NewRunnerGroupFromProperties(enterprise, organization, group string) RunnerGroup {
|
|
var scope RunnerGroupScope
|
|
|
|
if enterprise != "" {
|
|
scope = Enterprise
|
|
} else {
|
|
scope = Organization
|
|
}
|
|
|
|
return newRunnerGroup(scope, group)
|
|
}
|
|
|
|
// newRunnerGroup creates a new RunnerGroup instance from the provided arguments.
|
|
// There's a convention that an empty name implies a default runner group.
|
|
func newRunnerGroup(scope RunnerGroupScope, name string) RunnerGroup {
|
|
if name == "" {
|
|
return RunnerGroup{
|
|
Scope: scope,
|
|
Kind: Default,
|
|
Name: "",
|
|
}
|
|
}
|
|
|
|
return RunnerGroup{
|
|
Scope: scope,
|
|
Kind: Custom,
|
|
Name: name,
|
|
}
|
|
}
|
|
|
|
type RunnerGroup struct {
|
|
Scope RunnerGroupScope
|
|
Kind RunnerGroupKind
|
|
Name string
|
|
}
|
|
|
|
// VisibleRunnerGroups is a set of enterprise and organization runner groups
|
|
// that are visible to a GitHub repository.
|
|
// GitHub Actions chooses one of such visible group on which the workflow job is scheduled.
|
|
// ARC chooses the same group as Actions as the scale target.
|
|
type VisibleRunnerGroups struct {
|
|
// sortedGroups is a pointer to a mutable list of RunnerGroups that contains all the runner sortedGroups
|
|
// that are visible to the repository, including organization sortedGroups defined at the organization level,
|
|
// and enterprise sortedGroups that are inherited down to the organization.
|
|
sortedGroups []RunnerGroup
|
|
}
|
|
|
|
func NewVisibleRunnerGroups() *VisibleRunnerGroups {
|
|
return &VisibleRunnerGroups{}
|
|
}
|
|
|
|
func (g *VisibleRunnerGroups) IsEmpty() bool {
|
|
return len(g.sortedGroups) == 0
|
|
}
|
|
|
|
func (r *VisibleRunnerGroups) Includes(ref RunnerGroup) bool {
|
|
for _, r := range r.sortedGroups {
|
|
if r.Scope == ref.Scope && r.Kind == ref.Kind && r.Name == ref.Name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Add adds a runner group into VisibleRunnerGroups
|
|
// at a certain position in the list so that
|
|
// Traverse can return runner groups in order of higher precedence to lower precedence.
|
|
func (g *VisibleRunnerGroups) Add(rg RunnerGroup) error {
|
|
n := len(g.sortedGroups)
|
|
i := sort.Search(n, func(i int) bool {
|
|
data := g.sortedGroups[i]
|
|
|
|
if rg.Kind > data.Kind {
|
|
return false
|
|
} else if rg.Kind < data.Kind {
|
|
return true
|
|
}
|
|
|
|
if rg.Scope > data.Scope {
|
|
return false
|
|
} else if rg.Scope < data.Scope {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
})
|
|
|
|
g.insert(rg, i)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *VisibleRunnerGroups) insert(rg RunnerGroup, i int) {
|
|
var result []RunnerGroup
|
|
|
|
result = append(result, g.sortedGroups[:i]...)
|
|
result = append(result, rg)
|
|
result = append(result, g.sortedGroups[i:]...)
|
|
|
|
g.sortedGroups = result
|
|
}
|
|
|
|
// Traverse traverses all the runner groups visible to a repository
|
|
// in order of higher precedence to lower precedence.
|
|
func (g *VisibleRunnerGroups) Traverse(f func(RunnerGroup) (bool, error)) error {
|
|
for _, rg := range g.sortedGroups {
|
|
ok, err := f(rg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ok {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|