2022-05-05 13:25:50 +00:00
|
|
|
// Copyright 2022 Cloudbase Solutions SRL
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
|
|
|
// not use this file except in compliance with the License. You may obtain
|
|
|
|
|
// a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
|
// License for the specific language governing permissions and limitations
|
|
|
|
|
// under the License.
|
|
|
|
|
|
2022-05-04 16:27:24 +00:00
|
|
|
package runner
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2022-10-20 17:22:47 +03:00
|
|
|
"fmt"
|
2024-01-05 23:32:16 +00:00
|
|
|
"log/slog"
|
2022-05-05 13:25:50 +00:00
|
|
|
"strings"
|
|
|
|
|
|
2024-02-22 16:54:38 +01:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
|
2023-07-22 22:26:47 +00:00
|
|
|
runnerErrors "github.com/cloudbase/garm-provider-common/errors"
|
2023-03-12 16:01:49 +02:00
|
|
|
"github.com/cloudbase/garm/auth"
|
|
|
|
|
"github.com/cloudbase/garm/params"
|
|
|
|
|
"github.com/cloudbase/garm/runner/common"
|
2023-03-14 14:15:10 +02:00
|
|
|
"github.com/cloudbase/garm/util/appdefaults"
|
2022-05-04 16:27:24 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (r *Runner) CreateOrganization(ctx context.Context, param params.CreateOrgParams) (org params.Organization, err error) {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return org, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := param.Validate(); err != nil {
|
|
|
|
|
return params.Organization{}, errors.Wrap(err, "validating params")
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-16 17:05:18 +00:00
|
|
|
creds, err := r.store.GetGithubCredentialsByName(ctx, param.CredentialsName, true)
|
|
|
|
|
if err != nil {
|
2022-05-04 16:27:24 +00:00
|
|
|
return params.Organization{}, runnerErrors.NewBadRequestError("credentials %s not defined", param.CredentialsName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = r.store.GetOrganization(ctx, param.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if !errors.Is(err, runnerErrors.ErrNotFound) {
|
2022-08-10 12:15:12 +03:00
|
|
|
return params.Organization{}, errors.Wrap(err, "fetching org")
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return params.Organization{}, runnerErrors.NewConflictError("organization %s already exists", param.Name)
|
|
|
|
|
}
|
|
|
|
|
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
org, err = r.store.CreateOrganization(ctx, param.Name, creds.Name, param.WebhookSecret, param.PoolBalancerType)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return params.Organization{}, errors.Wrap(err, "creating organization")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
if err != nil {
|
2023-01-20 22:19:54 +02:00
|
|
|
if deleteErr := r.store.DeleteOrganization(ctx, org.ID); deleteErr != nil {
|
2024-01-05 23:32:16 +00:00
|
|
|
slog.With(slog.Any("error", deleteErr)).ErrorContext(
|
|
|
|
|
ctx, "failed to delete org",
|
|
|
|
|
"org_id", org.ID)
|
2023-01-20 22:19:54 +02:00
|
|
|
}
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
2024-04-24 12:23:45 +00:00
|
|
|
// Use the admin context in the pool manager. Any access control is already done above when
|
|
|
|
|
// updating the store.
|
2022-08-10 12:15:12 +03:00
|
|
|
poolMgr, err := r.poolManagerCtrl.CreateOrgPoolManager(r.ctx, org, r.providers, r.store)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return params.Organization{}, errors.Wrap(err, "creating org pool manager")
|
|
|
|
|
}
|
2022-05-04 16:27:24 +00:00
|
|
|
if err := poolMgr.Start(); err != nil {
|
2022-08-10 12:15:12 +03:00
|
|
|
if deleteErr := r.poolManagerCtrl.DeleteOrgPoolManager(org); deleteErr != nil {
|
2024-01-05 23:32:16 +00:00
|
|
|
slog.With(slog.Any("error", deleteErr)).ErrorContext(
|
|
|
|
|
ctx, "failed to cleanup pool manager for org",
|
|
|
|
|
"org_id", org.ID)
|
2022-08-10 12:15:12 +03:00
|
|
|
}
|
|
|
|
|
return params.Organization{}, errors.Wrap(err, "starting org pool manager")
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
return org, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) ListOrganizations(ctx context.Context) ([]params.Organization, error) {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return nil, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
orgs, err := r.store.ListOrganizations(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "listing organizations")
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-20 17:22:47 +03:00
|
|
|
var allOrgs []params.Organization
|
|
|
|
|
|
|
|
|
|
for _, org := range orgs {
|
|
|
|
|
poolMgr, err := r.poolManagerCtrl.GetOrgPoolManager(org)
|
|
|
|
|
if err != nil {
|
|
|
|
|
org.PoolManagerStatus.IsRunning = false
|
|
|
|
|
org.PoolManagerStatus.FailureReason = fmt.Sprintf("failed to get pool manager: %q", err)
|
|
|
|
|
} else {
|
|
|
|
|
org.PoolManagerStatus = poolMgr.Status()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
allOrgs = append(allOrgs, org)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return allOrgs, nil
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) GetOrganizationByID(ctx context.Context, orgID string) (params.Organization, error) {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return params.Organization{}, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
org, err := r.store.GetOrganizationByID(ctx, orgID)
|
|
|
|
|
if err != nil {
|
2022-08-10 12:15:12 +03:00
|
|
|
return params.Organization{}, errors.Wrap(err, "fetching organization")
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
2022-10-20 17:22:47 +03:00
|
|
|
|
|
|
|
|
poolMgr, err := r.poolManagerCtrl.GetOrgPoolManager(org)
|
|
|
|
|
if err != nil {
|
|
|
|
|
org.PoolManagerStatus.IsRunning = false
|
|
|
|
|
org.PoolManagerStatus.FailureReason = fmt.Sprintf("failed to get pool manager: %q", err)
|
|
|
|
|
}
|
|
|
|
|
org.PoolManagerStatus = poolMgr.Status()
|
2022-05-04 16:27:24 +00:00
|
|
|
return org, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-16 09:46:04 +00:00
|
|
|
func (r *Runner) DeleteOrganization(ctx context.Context, orgID string, keepWebhook bool) error {
|
2022-05-04 16:27:24 +00:00
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
org, err := r.store.GetOrganizationByID(ctx, orgID)
|
|
|
|
|
if err != nil {
|
2022-08-10 12:15:12 +03:00
|
|
|
return errors.Wrap(err, "fetching org")
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-29 18:18:29 +00:00
|
|
|
entity, err := org.GetEntity()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "getting entity")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pools, err := r.store.ListEntityPools(ctx, entity)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
2022-08-10 12:15:12 +03:00
|
|
|
return errors.Wrap(err, "fetching org pools")
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(pools) > 0 {
|
2024-02-22 17:28:39 +01:00
|
|
|
poolIDs := []string{}
|
2022-05-04 16:27:24 +00:00
|
|
|
for _, pool := range pools {
|
2024-02-22 17:28:39 +01:00
|
|
|
poolIDs = append(poolIDs, pool.ID)
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-22 17:28:39 +01:00
|
|
|
return runnerErrors.NewBadRequestError("org has pools defined (%s)", strings.Join(poolIDs, ", "))
|
2022-08-10 12:15:12 +03:00
|
|
|
}
|
|
|
|
|
|
2023-08-16 10:48:25 +00:00
|
|
|
if !keepWebhook && r.config.Default.EnableWebhookManagement {
|
2023-08-16 09:46:04 +00:00
|
|
|
poolMgr, err := r.poolManagerCtrl.GetOrgPoolManager(org)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "fetching pool manager")
|
|
|
|
|
}
|
2023-08-16 09:39:18 +00:00
|
|
|
|
2023-08-16 09:46:04 +00:00
|
|
|
if err := poolMgr.UninstallWebhook(ctx); err != nil {
|
2024-02-22 17:33:19 +01:00
|
|
|
// nolint:golangci-lint,godox
|
2023-08-16 09:46:04 +00:00
|
|
|
// TODO(gabriel-samfira): Should we error out here?
|
2024-01-05 23:32:16 +00:00
|
|
|
slog.With(slog.Any("error", err)).ErrorContext(
|
|
|
|
|
ctx, "failed to uninstall webhook",
|
|
|
|
|
"org_id", org.ID)
|
2023-08-16 09:46:04 +00:00
|
|
|
}
|
2023-08-16 09:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
2022-08-10 12:15:12 +03:00
|
|
|
if err := r.poolManagerCtrl.DeleteOrgPoolManager(org); err != nil {
|
|
|
|
|
return errors.Wrap(err, "deleting org pool manager")
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := r.store.DeleteOrganization(ctx, orgID); err != nil {
|
2022-05-04 21:57:08 +00:00
|
|
|
return errors.Wrapf(err, "removing organization %s", orgID)
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-04 23:47:55 +00:00
|
|
|
func (r *Runner) UpdateOrganization(ctx context.Context, orgID string, param params.UpdateEntityParams) (params.Organization, error) {
|
2022-05-04 16:27:24 +00:00
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return params.Organization{}, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r.mux.Lock()
|
|
|
|
|
defer r.mux.Unlock()
|
|
|
|
|
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
switch param.PoolBalancerType {
|
2024-03-15 07:26:04 +00:00
|
|
|
case params.PoolBalancerTypeRoundRobin, params.PoolBalancerTypePack, params.PoolBalancerTypeNone:
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
default:
|
|
|
|
|
return params.Organization{}, runnerErrors.NewBadRequestError("invalid pool balancer type: %s", param.PoolBalancerType)
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-16 17:05:18 +00:00
|
|
|
org, err := r.store.UpdateOrganization(ctx, orgID, param)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return params.Organization{}, errors.Wrap(err, "updating org")
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-24 12:23:45 +00:00
|
|
|
// Use the admin context in the pool manager. Any access control is already done above when
|
|
|
|
|
// updating the store.
|
2023-07-05 09:46:19 +00:00
|
|
|
poolMgr, err := r.poolManagerCtrl.UpdateOrgPoolManager(r.ctx, org)
|
2022-08-10 12:15:12 +03:00
|
|
|
if err != nil {
|
2023-07-05 09:46:19 +00:00
|
|
|
return params.Organization{}, fmt.Errorf("updating org pool manager: %w", err)
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-04 22:47:09 +00:00
|
|
|
org.PoolManagerStatus = poolMgr.Status()
|
2022-05-04 16:27:24 +00:00
|
|
|
return org, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) CreateOrgPool(ctx context.Context, orgID string, param params.CreatePoolParams) (params.Pool, error) {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return params.Pool{}, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createPoolParams, err := r.appendTagsToCreatePoolParams(param)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return params.Pool{}, errors.Wrap(err, "fetching pool params")
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-29 23:44:03 +00:00
|
|
|
if param.RunnerBootstrapTimeout == 0 {
|
2023-03-14 14:15:10 +02:00
|
|
|
param.RunnerBootstrapTimeout = appdefaults.DefaultRunnerBootstrapTimeout
|
2022-06-29 23:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 10:08:19 +00:00
|
|
|
entity := params.GithubEntity{
|
|
|
|
|
ID: orgID,
|
|
|
|
|
EntityType: params.GithubEntityTypeOrganization,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pool, err := r.store.CreateEntityPool(ctx, entity, createPoolParams)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return params.Pool{}, errors.Wrap(err, "creating pool")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pool, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) GetOrgPoolByID(ctx context.Context, orgID, poolID string) (params.Pool, error) {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return params.Pool{}, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 18:23:49 +00:00
|
|
|
entity := params.GithubEntity{
|
|
|
|
|
ID: orgID,
|
|
|
|
|
EntityType: params.GithubEntityTypeOrganization,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pool, err := r.store.GetEntityPool(ctx, entity, poolID)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
|
|
|
|
}
|
2024-03-28 18:23:49 +00:00
|
|
|
|
2022-05-04 16:27:24 +00:00
|
|
|
return pool, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) DeleteOrgPool(ctx context.Context, orgID, poolID string) error {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 18:23:49 +00:00
|
|
|
entity := params.GithubEntity{
|
|
|
|
|
ID: orgID,
|
|
|
|
|
EntityType: params.GithubEntityTypeOrganization,
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 18:23:49 +00:00
|
|
|
pool, err := r.store.GetEntityPool(ctx, entity, poolID)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
2024-03-28 18:23:49 +00:00
|
|
|
if !errors.Is(err, runnerErrors.ErrNotFound) {
|
|
|
|
|
return errors.Wrap(err, "fetching pool")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-22 17:33:19 +01:00
|
|
|
// nolint:golangci-lint,godox
|
2022-05-04 16:27:24 +00:00
|
|
|
// TODO: implement a count function
|
2024-03-28 18:23:49 +00:00
|
|
|
if len(pool.Instances) > 0 {
|
2022-05-04 16:27:24 +00:00
|
|
|
runnerIDs := []string{}
|
2024-03-28 18:23:49 +00:00
|
|
|
for _, run := range pool.Instances {
|
2022-05-04 16:27:24 +00:00
|
|
|
runnerIDs = append(runnerIDs, run.ID)
|
|
|
|
|
}
|
|
|
|
|
return runnerErrors.NewBadRequestError("pool has runners: %s", strings.Join(runnerIDs, ", "))
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 18:23:49 +00:00
|
|
|
if err := r.store.DeleteEntityPool(ctx, entity, poolID); err != nil {
|
2022-05-04 16:27:24 +00:00
|
|
|
return errors.Wrap(err, "deleting pool")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) ListOrgPools(ctx context.Context, orgID string) ([]params.Pool, error) {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return []params.Pool{}, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
2024-03-29 18:18:29 +00:00
|
|
|
entity := params.GithubEntity{
|
|
|
|
|
ID: orgID,
|
|
|
|
|
EntityType: params.GithubEntityTypeOrganization,
|
|
|
|
|
}
|
|
|
|
|
pools, err := r.store.ListEntityPools(ctx, entity)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "fetching pools")
|
|
|
|
|
}
|
|
|
|
|
return pools, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) UpdateOrgPool(ctx context.Context, orgID, poolID string, param params.UpdatePoolParams) (params.Pool, error) {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return params.Pool{}, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-28 18:23:49 +00:00
|
|
|
entity := params.GithubEntity{
|
|
|
|
|
ID: orgID,
|
|
|
|
|
EntityType: params.GithubEntityTypeOrganization,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pool, err := r.store.GetEntityPool(ctx, entity, poolID)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maxRunners := pool.MaxRunners
|
|
|
|
|
minIdleRunners := pool.MinIdleRunners
|
|
|
|
|
|
|
|
|
|
if param.MaxRunners != nil {
|
|
|
|
|
maxRunners = *param.MaxRunners
|
|
|
|
|
}
|
|
|
|
|
if param.MinIdleRunners != nil {
|
|
|
|
|
minIdleRunners = *param.MinIdleRunners
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if minIdleRunners > maxRunners {
|
|
|
|
|
return params.Pool{}, runnerErrors.NewBadRequestError("min_idle_runners cannot be larger than max_runners")
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-29 18:18:29 +00:00
|
|
|
newPool, err := r.store.UpdateEntityPool(ctx, entity, poolID, param)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return params.Pool{}, errors.Wrap(err, "updating pool")
|
|
|
|
|
}
|
|
|
|
|
return newPool, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) ListOrgInstances(ctx context.Context, orgID string) ([]params.Instance, error) {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return nil, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-29 18:18:29 +00:00
|
|
|
entity := params.GithubEntity{
|
|
|
|
|
ID: orgID,
|
|
|
|
|
EntityType: params.GithubEntityTypeOrganization,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instances, err := r.store.ListEntityInstances(ctx, entity)
|
2022-05-04 16:27:24 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return []params.Instance{}, errors.Wrap(err, "fetching instances")
|
|
|
|
|
}
|
|
|
|
|
return instances, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) findOrgPoolManager(name string) (common.PoolManager, error) {
|
|
|
|
|
r.mux.Lock()
|
|
|
|
|
defer r.mux.Unlock()
|
|
|
|
|
|
|
|
|
|
org, err := r.store.GetOrganization(r.ctx, name)
|
|
|
|
|
if err != nil {
|
2022-08-10 12:15:12 +03:00
|
|
|
return nil, errors.Wrap(err, "fetching org")
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
2022-08-10 12:15:12 +03:00
|
|
|
poolManager, err := r.poolManagerCtrl.GetOrgPoolManager(org)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "fetching pool manager for org")
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
2022-08-10 12:15:12 +03:00
|
|
|
return poolManager, nil
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
2023-08-15 17:19:06 +00:00
|
|
|
|
2023-08-16 09:11:45 +00:00
|
|
|
func (r *Runner) InstallOrgWebhook(ctx context.Context, orgID string, param params.InstallWebhookParams) (params.HookInfo, error) {
|
2023-08-15 17:19:06 +00:00
|
|
|
if !auth.IsAdmin(ctx) {
|
2023-08-16 09:11:45 +00:00
|
|
|
return params.HookInfo{}, runnerErrors.ErrUnauthorized
|
2023-08-15 17:19:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
org, err := r.store.GetOrganizationByID(ctx, orgID)
|
|
|
|
|
if err != nil {
|
2023-08-16 09:11:45 +00:00
|
|
|
return params.HookInfo{}, errors.Wrap(err, "fetching org")
|
2023-08-15 17:19:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
poolMgr, err := r.poolManagerCtrl.GetOrgPoolManager(org)
|
|
|
|
|
if err != nil {
|
2023-08-16 09:11:45 +00:00
|
|
|
return params.HookInfo{}, errors.Wrap(err, "fetching pool manager for org")
|
2023-08-15 17:19:06 +00:00
|
|
|
}
|
|
|
|
|
|
2023-08-16 09:11:45 +00:00
|
|
|
info, err := poolMgr.InstallWebhook(ctx, param)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return params.HookInfo{}, errors.Wrap(err, "installing webhook")
|
2023-08-15 17:19:06 +00:00
|
|
|
}
|
2023-08-16 09:11:45 +00:00
|
|
|
return info, nil
|
2023-08-15 17:19:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) UninstallOrgWebhook(ctx context.Context, orgID string) error {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
org, err := r.store.GetOrganizationByID(ctx, orgID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "fetching org")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
poolMgr, err := r.poolManagerCtrl.GetOrgPoolManager(org)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "fetching pool manager for org")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := poolMgr.UninstallWebhook(ctx); err != nil {
|
|
|
|
|
return errors.Wrap(err, "uninstalling webhook")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2023-08-16 09:11:45 +00:00
|
|
|
|
|
|
|
|
func (r *Runner) GetOrgWebhookInfo(ctx context.Context, orgID string) (params.HookInfo, error) {
|
|
|
|
|
if !auth.IsAdmin(ctx) {
|
|
|
|
|
return params.HookInfo{}, runnerErrors.ErrUnauthorized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
org, err := r.store.GetOrganizationByID(ctx, orgID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return params.HookInfo{}, errors.Wrap(err, "fetching org")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
poolMgr, err := r.poolManagerCtrl.GetOrgPoolManager(org)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return params.HookInfo{}, errors.Wrap(err, "fetching pool manager for org")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info, err := poolMgr.GetWebhookInfo(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return params.HookInfo{}, errors.Wrap(err, "fetching webhook info")
|
|
|
|
|
}
|
|
|
|
|
return info, nil
|
|
|
|
|
}
|