Find runner groups that visible to repository using a single API call. (#1324)
The [ListRunnerGroup API](https://docs.github.com/en/rest/reference/actions#list-self-hosted-runner-groups-for-an-organization) now add a new query parameter `visible_to_repository`. We were doing `N+1` lookup when trying to find which runner group can be used for job from a certain repository. - List all runner groups - Loop through all groups to check repository access for each of them via [API](https://docs.github.com/en/rest/reference/actions#list-repository-access-to-a-self-hosted-runner-group-in-an-organization) The new query parameter `visible_to_repository` should allow us to get the runner groups with access in one call. Limitation: - The new query parameter is only supported in GitHub.com, which means anyone who uses ARC in GitHub Enterprise Server won't get this. - I am working on a PR to update `go-github` library to support the new parameter, but it will take a few weeks for a newer `go-github` to be released, so in the meantime, I am duplicating the implementation in ARC as well to support the new query parameter.
This commit is contained in:
parent
c3e280eadb
commit
ebe7d060cb
|
|
@ -265,6 +265,29 @@ func (c *Client) ListOrganizationRunnerGroups(ctx context.Context, org string) (
|
||||||
return runnerGroups, nil
|
return runnerGroups, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListOrganizationRunnerGroupsForRepository returns all the runner groups defined in the organization and
|
||||||
|
// inherited to the organization from an enterprise.
|
||||||
|
// We can remove this when google/go-github library is updated to support this.
|
||||||
|
func (c *Client) ListOrganizationRunnerGroupsForRepository(ctx context.Context, org, repo string) ([]*github.RunnerGroup, error) {
|
||||||
|
var runnerGroups []*github.RunnerGroup
|
||||||
|
|
||||||
|
opts := github.ListOptions{PerPage: 100}
|
||||||
|
for {
|
||||||
|
list, res, err := c.listOrganizationRunnerGroupsVisibleToRepo(ctx, org, repo, &opts)
|
||||||
|
if err != nil {
|
||||||
|
return runnerGroups, fmt.Errorf("failed to list organization runner groups: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
runnerGroups = append(runnerGroups, list.RunnerGroups...)
|
||||||
|
if res.NextPage == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
opts.Page = res.NextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
return runnerGroups, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) ListRunnerGroupRepositoryAccesses(ctx context.Context, org string, runnerGroupId int64) ([]*github.Repository, error) {
|
func (c *Client) ListRunnerGroupRepositoryAccesses(ctx context.Context, org string, runnerGroupId int64) ([]*github.Repository, error) {
|
||||||
var repos []*github.Repository
|
var repos []*github.Repository
|
||||||
|
|
||||||
|
|
@ -286,6 +309,42 @@ func (c *Client) ListRunnerGroupRepositoryAccesses(ctx context.Context, org stri
|
||||||
return repos, nil
|
return repos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listOrganizationRunnerGroupsVisibleToRepo lists all self-hosted runner groups configured in an organization which can be used by the repository.
|
||||||
|
//
|
||||||
|
// GitHub API docs: https://docs.github.com/en/rest/reference/actions#list-self-hosted-runner-groups-for-an-organization
|
||||||
|
func (c *Client) listOrganizationRunnerGroupsVisibleToRepo(ctx context.Context, org, repo string, opts *github.ListOptions) (*github.RunnerGroups, *github.Response, error) {
|
||||||
|
repoName := repo
|
||||||
|
parts := strings.Split(repo, "/")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
repoName = parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
u := fmt.Sprintf("orgs/%v/actions/runner-groups?visible_to_repository=%v", org, repoName)
|
||||||
|
|
||||||
|
if opts != nil {
|
||||||
|
if opts.PerPage > 0 {
|
||||||
|
u = fmt.Sprintf("%v&per_page=%v", u, opts.PerPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Page > 0 {
|
||||||
|
u = fmt.Sprintf("%v&page=%v", u, opts.Page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := c.Client.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
groups := &github.RunnerGroups{}
|
||||||
|
resp, err := c.Client.Do(ctx, req, &groups)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
// cleanup removes expired registration tokens.
|
// cleanup removes expired registration tokens.
|
||||||
func (c *Client) cleanup() {
|
func (c *Client) cleanup() {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
|
|
|
||||||
|
|
@ -18,30 +18,47 @@ func (c *Simulator) GetRunnerGroupsVisibleToRepository(ctx context.Context, org,
|
||||||
panic(fmt.Sprintf("BUG: owner should not be empty in this context. repo=%v", repo))
|
panic(fmt.Sprintf("BUG: owner should not be empty in this context. repo=%v", repo))
|
||||||
}
|
}
|
||||||
|
|
||||||
runnerGroups, err := c.Client.ListOrganizationRunnerGroups(ctx, org)
|
if c.Client.GithubBaseURL == "https://github.com/" {
|
||||||
if err != nil {
|
runnerGroups, err := c.Client.ListOrganizationRunnerGroupsForRepository(ctx, org, repo)
|
||||||
return visible, err
|
if err != nil {
|
||||||
}
|
return visible, err
|
||||||
|
|
||||||
for _, runnerGroup := range runnerGroups {
|
|
||||||
ref := NewRunnerGroupFromGitHub(runnerGroup)
|
|
||||||
|
|
||||||
if !managed.Includes(ref) {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if runnerGroup.GetVisibility() != "all" {
|
for _, runnerGroup := range runnerGroups {
|
||||||
hasAccess, err := c.hasRepoAccessToOrganizationRunnerGroup(ctx, org, runnerGroup.GetID(), repo)
|
ref := NewRunnerGroupFromGitHub(runnerGroup)
|
||||||
if err != nil {
|
|
||||||
return visible, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hasAccess {
|
if !managed.Includes(ref) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
visible.Add(ref)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runnerGroups, err := c.Client.ListOrganizationRunnerGroups(ctx, org)
|
||||||
|
if err != nil {
|
||||||
|
return visible, err
|
||||||
}
|
}
|
||||||
|
|
||||||
visible.Add(ref)
|
for _, runnerGroup := range runnerGroups {
|
||||||
|
ref := NewRunnerGroupFromGitHub(runnerGroup)
|
||||||
|
|
||||||
|
if !managed.Includes(ref) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if runnerGroup.GetVisibility() != "all" {
|
||||||
|
hasAccess, err := c.hasRepoAccessToOrganizationRunnerGroup(ctx, org, runnerGroup.GetID(), repo)
|
||||||
|
if err != nil {
|
||||||
|
return visible, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAccess {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visible.Add(ref)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return visible, nil
|
return visible, nil
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue