Merge pull request #235 from gabriel-samfira/refactor-internal-gh-client
Refactor internal gh client
This commit is contained in:
commit
90dd92d27c
21 changed files with 1446 additions and 2197 deletions
|
|
@ -39,14 +39,14 @@ type InstanceJWTClaims struct {
|
|||
Name string `json:"name"`
|
||||
PoolID string `json:"provider_id"`
|
||||
// Scope is either repository or organization
|
||||
Scope params.PoolType `json:"scope"`
|
||||
Scope params.GithubEntityType `json:"scope"`
|
||||
// Entity is the repo or org name
|
||||
Entity string `json:"entity"`
|
||||
CreateAttempt int `json:"create_attempt"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func NewInstanceJWTToken(instance params.Instance, secret, entity string, poolType params.PoolType, ttlMinutes uint) (string, error) {
|
||||
func NewInstanceJWTToken(instance params.Instance, secret, entity string, poolType params.GithubEntityType, ttlMinutes uint) (string, error) {
|
||||
// Token expiration is equal to the bootstrap timeout set on the pool plus the polling
|
||||
// interval garm uses to check for timed out runners. Runners that have not sent their info
|
||||
// by the end of this interval are most likely failed and will be reaped by garm anyway.
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ type PoolStore interface {
|
|||
|
||||
PoolInstanceCount(ctx context.Context, poolID string) (int64, error)
|
||||
GetPoolInstanceByName(ctx context.Context, poolID string, instanceName string) (params.Instance, error)
|
||||
FindPoolsMatchingAllTags(ctx context.Context, entityType params.PoolType, entityID string, tags []string) ([]params.Pool, error)
|
||||
FindPoolsMatchingAllTags(ctx context.Context, entityType params.GithubEntityType, entityID string, tags []string) ([]params.Pool, error)
|
||||
}
|
||||
|
||||
type UserStore interface {
|
||||
|
|
@ -117,7 +117,7 @@ type InstanceStore interface {
|
|||
|
||||
type JobsStore interface {
|
||||
CreateOrUpdateJob(ctx context.Context, job params.Job) (params.Job, error)
|
||||
ListEntityJobsByStatus(ctx context.Context, entityType params.PoolType, entityID string, status params.JobStatus) ([]params.Job, error)
|
||||
ListEntityJobsByStatus(ctx context.Context, entityType params.GithubEntityType, entityID string, status params.JobStatus) ([]params.Job, error)
|
||||
ListJobsByStatus(ctx context.Context, status params.JobStatus) ([]params.Job, error)
|
||||
ListAllJobs(ctx context.Context) ([]params.Job, error)
|
||||
|
||||
|
|
|
|||
|
|
@ -567,7 +567,7 @@ func (_m *Store) FindOrganizationPoolByTags(ctx context.Context, orgID string, t
|
|||
}
|
||||
|
||||
// FindPoolsMatchingAllTags provides a mock function with given fields: ctx, entityType, entityID, tags
|
||||
func (_m *Store) FindPoolsMatchingAllTags(ctx context.Context, entityType params.PoolType, entityID string, tags []string) ([]params.Pool, error) {
|
||||
func (_m *Store) FindPoolsMatchingAllTags(ctx context.Context, entityType params.GithubEntityType, entityID string, tags []string) ([]params.Pool, error) {
|
||||
ret := _m.Called(ctx, entityType, entityID, tags)
|
||||
|
||||
if len(ret) == 0 {
|
||||
|
|
@ -576,10 +576,10 @@ func (_m *Store) FindPoolsMatchingAllTags(ctx context.Context, entityType params
|
|||
|
||||
var r0 []params.Pool
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, params.PoolType, string, []string) ([]params.Pool, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntityType, string, []string) ([]params.Pool, error)); ok {
|
||||
return rf(ctx, entityType, entityID, tags)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, params.PoolType, string, []string) []params.Pool); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntityType, string, []string) []params.Pool); ok {
|
||||
r0 = rf(ctx, entityType, entityID, tags)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
|
|
@ -587,7 +587,7 @@ func (_m *Store) FindPoolsMatchingAllTags(ctx context.Context, entityType params
|
|||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, params.PoolType, string, []string) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntityType, string, []string) error); ok {
|
||||
r1 = rf(ctx, entityType, entityID, tags)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
|
|
@ -1271,7 +1271,7 @@ func (_m *Store) ListEnterprises(ctx context.Context) ([]params.Enterprise, erro
|
|||
}
|
||||
|
||||
// ListEntityJobsByStatus provides a mock function with given fields: ctx, entityType, entityID, status
|
||||
func (_m *Store) ListEntityJobsByStatus(ctx context.Context, entityType params.PoolType, entityID string, status params.JobStatus) ([]params.Job, error) {
|
||||
func (_m *Store) ListEntityJobsByStatus(ctx context.Context, entityType params.GithubEntityType, entityID string, status params.JobStatus) ([]params.Job, error) {
|
||||
ret := _m.Called(ctx, entityType, entityID, status)
|
||||
|
||||
if len(ret) == 0 {
|
||||
|
|
@ -1280,10 +1280,10 @@ func (_m *Store) ListEntityJobsByStatus(ctx context.Context, entityType params.P
|
|||
|
||||
var r0 []params.Job
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, params.PoolType, string, params.JobStatus) ([]params.Job, error)); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntityType, string, params.JobStatus) ([]params.Job, error)); ok {
|
||||
return rf(ctx, entityType, entityID, status)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, params.PoolType, string, params.JobStatus) []params.Job); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntityType, string, params.JobStatus) []params.Job); ok {
|
||||
r0 = rf(ctx, entityType, entityID, status)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
|
|
@ -1291,7 +1291,7 @@ func (_m *Store) ListEntityJobsByStatus(ctx context.Context, entityType params.P
|
|||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, params.PoolType, string, params.JobStatus) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntityType, string, params.JobStatus) error); ok {
|
||||
r1 = rf(ctx, entityType, entityID, status)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ func (s *sqlDatabase) CreateEnterprisePool(ctx context.Context, enterpriseID str
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) GetEnterprisePool(ctx context.Context, enterpriseID, poolID string) (params.Pool, error) {
|
||||
pool, err := s.getEntityPool(ctx, params.EnterprisePool, enterpriseID, poolID, "Tags", "Instances")
|
||||
pool, err := s.getEntityPool(ctx, params.GithubEntityTypeEnterprise, enterpriseID, poolID, "Tags", "Instances")
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -210,7 +210,7 @@ func (s *sqlDatabase) GetEnterprisePool(ctx context.Context, enterpriseID, poolI
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) DeleteEnterprisePool(ctx context.Context, enterpriseID, poolID string) error {
|
||||
pool, err := s.getEntityPool(ctx, params.EnterprisePool, enterpriseID, poolID)
|
||||
pool, err := s.getEntityPool(ctx, params.GithubEntityTypeEnterprise, enterpriseID, poolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "looking up enterprise pool")
|
||||
}
|
||||
|
|
@ -222,7 +222,7 @@ func (s *sqlDatabase) DeleteEnterprisePool(ctx context.Context, enterpriseID, po
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) UpdateEnterprisePool(ctx context.Context, enterpriseID, poolID string, param params.UpdatePoolParams) (params.Pool, error) {
|
||||
pool, err := s.getEntityPool(ctx, params.EnterprisePool, enterpriseID, poolID, "Tags", "Instances", "Enterprise", "Organization", "Repository")
|
||||
pool, err := s.getEntityPool(ctx, params.GithubEntityTypeEnterprise, enterpriseID, poolID, "Tags", "Instances", "Enterprise", "Organization", "Repository")
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -231,7 +231,7 @@ func (s *sqlDatabase) UpdateEnterprisePool(ctx context.Context, enterpriseID, po
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) FindEnterprisePoolByTags(_ context.Context, enterpriseID string, tags []string) (params.Pool, error) {
|
||||
pool, err := s.findPoolByTags(enterpriseID, params.EnterprisePool, tags)
|
||||
pool, err := s.findPoolByTags(enterpriseID, params.GithubEntityTypeEnterprise, tags)
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -239,7 +239,7 @@ func (s *sqlDatabase) FindEnterprisePoolByTags(_ context.Context, enterpriseID s
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) ListEnterprisePools(ctx context.Context, enterpriseID string) ([]params.Pool, error) {
|
||||
pools, err := s.listEntityPools(ctx, params.EnterprisePool, enterpriseID, "Tags", "Instances", "Enterprise")
|
||||
pools, err := s.listEntityPools(ctx, params.GithubEntityTypeEnterprise, enterpriseID, "Tags", "Instances", "Enterprise")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching pools")
|
||||
}
|
||||
|
|
@ -256,7 +256,7 @@ func (s *sqlDatabase) ListEnterprisePools(ctx context.Context, enterpriseID stri
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) ListEnterpriseInstances(ctx context.Context, enterpriseID string) ([]params.Instance, error) {
|
||||
pools, err := s.listEntityPools(ctx, params.EnterprisePool, enterpriseID, "Instances", "Tags", "Instances.Job")
|
||||
pools, err := s.listEntityPools(ctx, params.GithubEntityTypeEnterprise, enterpriseID, "Instances", "Tags", "Instances.Job")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching enterprise")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ func (s *sqlDatabase) ListJobsByStatus(_ context.Context, status params.JobStatu
|
|||
}
|
||||
|
||||
// ListEntityJobsByStatus lists all jobs for a given entity type and id.
|
||||
func (s *sqlDatabase) ListEntityJobsByStatus(_ context.Context, entityType params.PoolType, entityID string, status params.JobStatus) ([]params.Job, error) {
|
||||
func (s *sqlDatabase) ListEntityJobsByStatus(_ context.Context, entityType params.GithubEntityType, entityID string, status params.JobStatus) ([]params.Job, error) {
|
||||
u, err := uuid.Parse(entityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -281,11 +281,11 @@ func (s *sqlDatabase) ListEntityJobsByStatus(_ context.Context, entityType param
|
|||
query := s.conn.Model(&WorkflowJob{}).Preload("Instance").Where("status = ?", status)
|
||||
|
||||
switch entityType {
|
||||
case params.OrganizationPool:
|
||||
case params.GithubEntityTypeOrganization:
|
||||
query = query.Where("org_id = ?", u)
|
||||
case params.RepositoryPool:
|
||||
case params.GithubEntityTypeRepository:
|
||||
query = query.Where("repo_id = ?", u)
|
||||
case params.EnterprisePool:
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
query = query.Where("enterprise_id = ?", u)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ func (s *sqlDatabase) CreateOrganizationPool(ctx context.Context, orgID string,
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) ListOrgPools(ctx context.Context, orgID string) ([]params.Pool, error) {
|
||||
pools, err := s.listEntityPools(ctx, params.OrganizationPool, orgID, "Tags", "Instances", "Organization")
|
||||
pools, err := s.listEntityPools(ctx, params.GithubEntityTypeOrganization, orgID, "Tags", "Instances", "Organization")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching pools")
|
||||
}
|
||||
|
|
@ -236,7 +236,7 @@ func (s *sqlDatabase) ListOrgPools(ctx context.Context, orgID string) ([]params.
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) GetOrganizationPool(ctx context.Context, orgID, poolID string) (params.Pool, error) {
|
||||
pool, err := s.getEntityPool(ctx, params.OrganizationPool, orgID, poolID, "Tags", "Instances")
|
||||
pool, err := s.getEntityPool(ctx, params.GithubEntityTypeOrganization, orgID, poolID, "Tags", "Instances")
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -244,7 +244,7 @@ func (s *sqlDatabase) GetOrganizationPool(ctx context.Context, orgID, poolID str
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) DeleteOrganizationPool(ctx context.Context, orgID, poolID string) error {
|
||||
pool, err := s.getEntityPool(ctx, params.OrganizationPool, orgID, poolID)
|
||||
pool, err := s.getEntityPool(ctx, params.GithubEntityTypeOrganization, orgID, poolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "looking up org pool")
|
||||
}
|
||||
|
|
@ -256,7 +256,7 @@ func (s *sqlDatabase) DeleteOrganizationPool(ctx context.Context, orgID, poolID
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) FindOrganizationPoolByTags(_ context.Context, orgID string, tags []string) (params.Pool, error) {
|
||||
pool, err := s.findPoolByTags(orgID, params.OrganizationPool, tags)
|
||||
pool, err := s.findPoolByTags(orgID, params.GithubEntityTypeOrganization, tags)
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -264,7 +264,7 @@ func (s *sqlDatabase) FindOrganizationPoolByTags(_ context.Context, orgID string
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) ListOrgInstances(ctx context.Context, orgID string) ([]params.Instance, error) {
|
||||
pools, err := s.listEntityPools(ctx, params.OrganizationPool, orgID, "Tags", "Instances", "Instances.Job")
|
||||
pools, err := s.listEntityPools(ctx, params.GithubEntityTypeOrganization, orgID, "Tags", "Instances", "Instances.Job")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching org")
|
||||
}
|
||||
|
|
@ -282,7 +282,7 @@ func (s *sqlDatabase) ListOrgInstances(ctx context.Context, orgID string) ([]par
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) UpdateOrganizationPool(ctx context.Context, orgID, poolID string, param params.UpdatePoolParams) (params.Pool, error) {
|
||||
pool, err := s.getEntityPool(ctx, params.OrganizationPool, orgID, poolID, "Tags", "Instances", "Enterprise", "Organization", "Repository")
|
||||
pool, err := s.getEntityPool(ctx, params.GithubEntityTypeOrganization, orgID, poolID, "Tags", "Instances", "Enterprise", "Organization", "Repository")
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ func (s *sqlDatabase) DeletePoolByID(ctx context.Context, poolID string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *sqlDatabase) getEntityPool(_ context.Context, entityType params.PoolType, entityID, poolID string, preload ...string) (Pool, error) {
|
||||
func (s *sqlDatabase) getEntityPool(_ context.Context, entityType params.GithubEntityType, entityID, poolID string, preload ...string) (Pool, error) {
|
||||
if entityID == "" {
|
||||
return Pool{}, errors.Wrap(runnerErrors.ErrBadRequest, "missing entity id")
|
||||
}
|
||||
|
|
@ -97,11 +97,11 @@ func (s *sqlDatabase) getEntityPool(_ context.Context, entityType params.PoolTyp
|
|||
|
||||
var fieldName string
|
||||
switch entityType {
|
||||
case params.RepositoryPool:
|
||||
case params.GithubEntityTypeRepository:
|
||||
fieldName = entityTypeRepoName
|
||||
case params.OrganizationPool:
|
||||
case params.GithubEntityTypeOrganization:
|
||||
fieldName = entityTypeOrgName
|
||||
case params.EnterprisePool:
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
fieldName = entityTypeEnterpriseName
|
||||
default:
|
||||
return Pool{}, fmt.Errorf("invalid entityType: %v", entityType)
|
||||
|
|
@ -122,7 +122,7 @@ func (s *sqlDatabase) getEntityPool(_ context.Context, entityType params.PoolTyp
|
|||
return pool, nil
|
||||
}
|
||||
|
||||
func (s *sqlDatabase) listEntityPools(_ context.Context, entityType params.PoolType, entityID string, preload ...string) ([]Pool, error) {
|
||||
func (s *sqlDatabase) listEntityPools(_ context.Context, entityType params.GithubEntityType, entityID string, preload ...string) ([]Pool, error) {
|
||||
if _, err := uuid.Parse(entityID); err != nil {
|
||||
return nil, errors.Wrap(runnerErrors.ErrBadRequest, "parsing id")
|
||||
}
|
||||
|
|
@ -136,11 +136,11 @@ func (s *sqlDatabase) listEntityPools(_ context.Context, entityType params.PoolT
|
|||
|
||||
var fieldName string
|
||||
switch entityType {
|
||||
case params.RepositoryPool:
|
||||
case params.GithubEntityTypeRepository:
|
||||
fieldName = entityTypeRepoName
|
||||
case params.OrganizationPool:
|
||||
case params.GithubEntityTypeOrganization:
|
||||
fieldName = entityTypeOrgName
|
||||
case params.EnterprisePool:
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
fieldName = entityTypeEnterpriseName
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid entityType: %v", entityType)
|
||||
|
|
@ -162,7 +162,7 @@ func (s *sqlDatabase) listEntityPools(_ context.Context, entityType params.PoolT
|
|||
return pools, nil
|
||||
}
|
||||
|
||||
func (s *sqlDatabase) findPoolByTags(id string, poolType params.PoolType, tags []string) ([]params.Pool, error) {
|
||||
func (s *sqlDatabase) findPoolByTags(id string, poolType params.GithubEntityType, tags []string) ([]params.Pool, error) {
|
||||
if len(tags) == 0 {
|
||||
return nil, runnerErrors.NewBadRequestError("missing tags")
|
||||
}
|
||||
|
|
@ -173,11 +173,11 @@ func (s *sqlDatabase) findPoolByTags(id string, poolType params.PoolType, tags [
|
|||
|
||||
var fieldName string
|
||||
switch poolType {
|
||||
case params.RepositoryPool:
|
||||
case params.GithubEntityTypeRepository:
|
||||
fieldName = entityTypeRepoName
|
||||
case params.OrganizationPool:
|
||||
case params.GithubEntityTypeOrganization:
|
||||
fieldName = entityTypeOrgName
|
||||
case params.EnterprisePool:
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
fieldName = entityTypeEnterpriseName
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid poolType: %v", poolType)
|
||||
|
|
@ -216,7 +216,7 @@ func (s *sqlDatabase) findPoolByTags(id string, poolType params.PoolType, tags [
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *sqlDatabase) FindPoolsMatchingAllTags(_ context.Context, entityType params.PoolType, entityID string, tags []string) ([]params.Pool, error) {
|
||||
func (s *sqlDatabase) FindPoolsMatchingAllTags(_ context.Context, entityType params.GithubEntityType, entityID string, tags []string) ([]params.Pool, error) {
|
||||
if len(tags) == 0 {
|
||||
return nil, runnerErrors.NewBadRequestError("missing tags")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ func (s *sqlDatabase) CreateRepositoryPool(ctx context.Context, repoID string, p
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) ListRepoPools(ctx context.Context, repoID string) ([]params.Pool, error) {
|
||||
pools, err := s.listEntityPools(ctx, params.RepositoryPool, repoID, "Tags", "Instances", "Repository")
|
||||
pools, err := s.listEntityPools(ctx, params.GithubEntityTypeRepository, repoID, "Tags", "Instances", "Repository")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching pools")
|
||||
}
|
||||
|
|
@ -236,7 +236,7 @@ func (s *sqlDatabase) ListRepoPools(ctx context.Context, repoID string) ([]param
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) GetRepositoryPool(ctx context.Context, repoID, poolID string) (params.Pool, error) {
|
||||
pool, err := s.getEntityPool(ctx, params.RepositoryPool, repoID, poolID, "Tags", "Instances")
|
||||
pool, err := s.getEntityPool(ctx, params.GithubEntityTypeRepository, repoID, poolID, "Tags", "Instances")
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -244,7 +244,7 @@ func (s *sqlDatabase) GetRepositoryPool(ctx context.Context, repoID, poolID stri
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) DeleteRepositoryPool(ctx context.Context, repoID, poolID string) error {
|
||||
pool, err := s.getEntityPool(ctx, params.RepositoryPool, repoID, poolID)
|
||||
pool, err := s.getEntityPool(ctx, params.GithubEntityTypeRepository, repoID, poolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "looking up repo pool")
|
||||
}
|
||||
|
|
@ -256,7 +256,7 @@ func (s *sqlDatabase) DeleteRepositoryPool(ctx context.Context, repoID, poolID s
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) FindRepositoryPoolByTags(_ context.Context, repoID string, tags []string) (params.Pool, error) {
|
||||
pool, err := s.findPoolByTags(repoID, params.RepositoryPool, tags)
|
||||
pool, err := s.findPoolByTags(repoID, params.GithubEntityTypeRepository, tags)
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -264,7 +264,7 @@ func (s *sqlDatabase) FindRepositoryPoolByTags(_ context.Context, repoID string,
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) ListRepoInstances(ctx context.Context, repoID string) ([]params.Instance, error) {
|
||||
pools, err := s.listEntityPools(ctx, params.RepositoryPool, repoID, "Tags", "Instances", "Instances.Job")
|
||||
pools, err := s.listEntityPools(ctx, params.GithubEntityTypeRepository, repoID, "Tags", "Instances", "Instances.Job")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching repo")
|
||||
}
|
||||
|
|
@ -283,7 +283,7 @@ func (s *sqlDatabase) ListRepoInstances(ctx context.Context, repoID string) ([]p
|
|||
}
|
||||
|
||||
func (s *sqlDatabase) UpdateRepositoryPool(ctx context.Context, repoID, poolID string, param params.UpdatePoolParams) (params.Pool, error) {
|
||||
pool, err := s.getEntityPool(ctx, params.RepositoryPool, repoID, poolID, "Tags", "Instances", "Enterprise", "Organization", "Repository")
|
||||
pool, err := s.getEntityPool(ctx, params.GithubEntityTypeRepository, repoID, poolID, "Tags", "Instances", "Enterprise", "Organization", "Repository")
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
PoolType string
|
||||
GithubEntityType string
|
||||
EventType string
|
||||
EventLevel string
|
||||
ProviderType string
|
||||
|
|
@ -81,9 +81,15 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
RepositoryPool PoolType = "repository"
|
||||
OrganizationPool PoolType = "organization"
|
||||
EnterprisePool PoolType = "enterprise"
|
||||
GithubEntityTypeRepository GithubEntityType = "repository"
|
||||
GithubEntityTypeOrganization GithubEntityType = "organization"
|
||||
GithubEntityTypeEnterprise GithubEntityType = "enterprise"
|
||||
)
|
||||
|
||||
const (
|
||||
MetricsLabelEnterpriseScope = "Enterprise"
|
||||
MetricsLabelRepositoryScope = "Repository"
|
||||
MetricsLabelOrganizationScope = "Organization"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -113,6 +119,10 @@ const (
|
|||
GithubAuthTypeApp GithubAuthType = "app"
|
||||
)
|
||||
|
||||
func (e GithubEntityType) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
type StatusMessage struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Message string `json:"message"`
|
||||
|
|
@ -318,14 +328,14 @@ func (p *Pool) RunnerTimeout() uint {
|
|||
return p.RunnerBootstrapTimeout
|
||||
}
|
||||
|
||||
func (p *Pool) PoolType() PoolType {
|
||||
func (p *Pool) PoolType() GithubEntityType {
|
||||
switch {
|
||||
case p.RepoID != "":
|
||||
return RepositoryPool
|
||||
return GithubEntityTypeRepository
|
||||
case p.OrgID != "":
|
||||
return OrganizationPool
|
||||
return GithubEntityTypeOrganization
|
||||
case p.EnterpriseID != "":
|
||||
return EnterprisePool
|
||||
return GithubEntityTypeEnterprise
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
@ -631,3 +641,34 @@ type UpdateSystemInfoParams struct {
|
|||
OSVersion string `json:"os_version,omitempty"`
|
||||
AgentID *int64 `json:"agent_id,omitempty"`
|
||||
}
|
||||
|
||||
type GithubEntity struct {
|
||||
Owner string `json:"owner"`
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
EntityType GithubEntityType `json:"entity_type"`
|
||||
|
||||
WebhookSecret string `json:"-"`
|
||||
}
|
||||
|
||||
func (g GithubEntity) LabelScope() string {
|
||||
switch g.EntityType {
|
||||
case GithubEntityTypeRepository:
|
||||
return MetricsLabelRepositoryScope
|
||||
case GithubEntityTypeOrganization:
|
||||
return MetricsLabelOrganizationScope
|
||||
case GithubEntityTypeEnterprise:
|
||||
return MetricsLabelEnterpriseScope
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (g GithubEntity) String() string {
|
||||
switch g.EntityType {
|
||||
case GithubEntityTypeRepository:
|
||||
return fmt.Sprintf("%s/%s", g.Owner, g.Name)
|
||||
case GithubEntityTypeOrganization, GithubEntityTypeEnterprise:
|
||||
return g.Owner
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
|
||||
github "github.com/google/go-github/v57/github"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
params "github.com/cloudbase/garm/params"
|
||||
)
|
||||
|
||||
// GithubClient is an autogenerated mock type for the GithubClient type
|
||||
|
|
@ -14,185 +16,29 @@ type GithubClient struct {
|
|||
mock.Mock
|
||||
}
|
||||
|
||||
// CreateOrgHook provides a mock function with given fields: ctx, org, hook
|
||||
func (_m *GithubClient) CreateOrgHook(ctx context.Context, org string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, hook)
|
||||
// CreateEntityHook provides a mock function with given fields: ctx, hook
|
||||
func (_m *GithubClient) CreateEntityHook(ctx context.Context, hook *github.Hook) (*github.Hook, error) {
|
||||
ret := _m.Called(ctx, hook)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateOrgHook")
|
||||
panic("no return value specified for CreateEntityHook")
|
||||
}
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.Hook) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, org, hook)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.Hook) *github.Hook); ok {
|
||||
r0 = rf(ctx, org, hook)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *github.Hook) *github.Response); ok {
|
||||
r1 = rf(ctx, org, hook)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, *github.Hook) error); ok {
|
||||
r2 = rf(ctx, org, hook)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// CreateOrganizationRegistrationToken provides a mock function with given fields: ctx, owner
|
||||
func (_m *GithubClient) CreateOrganizationRegistrationToken(ctx context.Context, owner string) (*github.RegistrationToken, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateOrganizationRegistrationToken")
|
||||
}
|
||||
|
||||
var r0 *github.RegistrationToken
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (*github.RegistrationToken, *github.Response, error)); ok {
|
||||
return rf(ctx, owner)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) *github.RegistrationToken); ok {
|
||||
r0 = rf(ctx, owner)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.RegistrationToken)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) *github.Response); ok {
|
||||
r1 = rf(ctx, owner)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string) error); ok {
|
||||
r2 = rf(ctx, owner)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// CreateRegistrationToken provides a mock function with given fields: ctx, owner, repo
|
||||
func (_m *GithubClient) CreateRegistrationToken(ctx context.Context, owner string, repo string) (*github.RegistrationToken, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateRegistrationToken")
|
||||
}
|
||||
|
||||
var r0 *github.RegistrationToken
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) (*github.RegistrationToken, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.RegistrationToken); ok {
|
||||
r0 = rf(ctx, owner, repo)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.RegistrationToken)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok {
|
||||
r2 = rf(ctx, owner, repo)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// CreateRepoHook provides a mock function with given fields: ctx, owner, repo, hook
|
||||
func (_m *GithubClient) CreateRepoHook(ctx context.Context, owner string, repo string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, hook)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateRepoHook")
|
||||
}
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.Hook) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, hook)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.Hook) *github.Hook); ok {
|
||||
r0 = rf(ctx, owner, repo, hook)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.Hook) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, hook)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.Hook) error); ok {
|
||||
r2 = rf(ctx, owner, repo, hook)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// DeleteOrgHook provides a mock function with given fields: ctx, org, id
|
||||
func (_m *GithubClient) DeleteOrgHook(ctx context.Context, org string, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, org, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteOrgHook")
|
||||
}
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, org, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.Hook) (*github.Hook, error)); ok {
|
||||
return rf(ctx, hook)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, org, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.Hook) *github.Hook); ok {
|
||||
r0 = rf(ctx, hook)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok {
|
||||
r1 = rf(ctx, org, id)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *github.Hook) error); ok {
|
||||
r1 = rf(ctx, hook)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
|
@ -200,29 +46,68 @@ func (_m *GithubClient) DeleteOrgHook(ctx context.Context, org string, id int64)
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteRepoHook provides a mock function with given fields: ctx, owner, repo, id
|
||||
func (_m *GithubClient) DeleteRepoHook(ctx context.Context, owner string, repo string, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, id)
|
||||
// CreateEntityRegistrationToken provides a mock function with given fields: ctx
|
||||
func (_m *GithubClient) CreateEntityRegistrationToken(ctx context.Context) (*github.RegistrationToken, *github.Response, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteRepoHook")
|
||||
panic("no return value specified for CreateEntityRegistrationToken")
|
||||
}
|
||||
|
||||
var r0 *github.RegistrationToken
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) (*github.RegistrationToken, *github.Response, error)); ok {
|
||||
return rf(ctx)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context) *github.RegistrationToken); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.RegistrationToken)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context) *github.Response); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context) error); ok {
|
||||
r2 = rf(ctx)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// DeleteEntityHook provides a mock function with given fields: ctx, id
|
||||
func (_m *GithubClient) DeleteEntityHook(ctx context.Context, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteEntityHook")
|
||||
}
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, owner, repo, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) error); ok {
|
||||
r1 = rf(ctx, owner, repo, id)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
|
@ -230,155 +115,68 @@ func (_m *GithubClient) DeleteRepoHook(ctx context.Context, owner string, repo s
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// GenerateOrgJITConfig provides a mock function with given fields: ctx, owner, request
|
||||
func (_m *GithubClient) GenerateOrgJITConfig(ctx context.Context, owner string, request *github.GenerateJITConfigRequest) (*github.JITRunnerConfig, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, request)
|
||||
// GetEntityHook provides a mock function with given fields: ctx, id
|
||||
func (_m *GithubClient) GetEntityHook(ctx context.Context, id int64) (*github.Hook, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GenerateOrgJITConfig")
|
||||
}
|
||||
|
||||
var r0 *github.JITRunnerConfig
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.GenerateJITConfigRequest) (*github.JITRunnerConfig, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, request)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.GenerateJITConfigRequest) *github.JITRunnerConfig); ok {
|
||||
r0 = rf(ctx, owner, request)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.JITRunnerConfig)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *github.GenerateJITConfigRequest) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, request)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, *github.GenerateJITConfigRequest) error); ok {
|
||||
r2 = rf(ctx, owner, request)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// GenerateRepoJITConfig provides a mock function with given fields: ctx, owner, repo, request
|
||||
func (_m *GithubClient) GenerateRepoJITConfig(ctx context.Context, owner string, repo string, request *github.GenerateJITConfigRequest) (*github.JITRunnerConfig, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, request)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GenerateRepoJITConfig")
|
||||
}
|
||||
|
||||
var r0 *github.JITRunnerConfig
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.GenerateJITConfigRequest) (*github.JITRunnerConfig, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, request)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.GenerateJITConfigRequest) *github.JITRunnerConfig); ok {
|
||||
r0 = rf(ctx, owner, repo, request)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.JITRunnerConfig)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.GenerateJITConfigRequest) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, request)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.GenerateJITConfigRequest) error); ok {
|
||||
r2 = rf(ctx, owner, repo, request)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// GetOrgHook provides a mock function with given fields: ctx, org, id
|
||||
func (_m *GithubClient) GetOrgHook(ctx context.Context, org string, id int64) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetOrgHook")
|
||||
panic("no return value specified for GetEntityHook")
|
||||
}
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, org, id)
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*github.Hook, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Hook); ok {
|
||||
r0 = rf(ctx, org, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *github.Hook); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, int64) *github.Response); ok {
|
||||
r1 = rf(ctx, org, id)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, int64) error); ok {
|
||||
r2 = rf(ctx, org, id)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetRepoHook provides a mock function with given fields: ctx, owner, repo, id
|
||||
func (_m *GithubClient) GetRepoHook(ctx context.Context, owner string, repo string, id int64) (*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, id)
|
||||
// GetEntityJITConfig provides a mock function with given fields: ctx, instance, pool, labels
|
||||
func (_m *GithubClient) GetEntityJITConfig(ctx context.Context, instance string, pool params.Pool, labels []string) (map[string]string, *github.Runner, error) {
|
||||
ret := _m.Called(ctx, instance, pool, labels)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetRepoHook")
|
||||
panic("no return value specified for GetEntityJITConfig")
|
||||
}
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 *github.Response
|
||||
var r0 map[string]string
|
||||
var r1 *github.Runner
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, params.Pool, []string) (map[string]string, *github.Runner, error)); ok {
|
||||
return rf(ctx, instance, pool, labels)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *github.Hook); ok {
|
||||
r0 = rf(ctx, owner, repo, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, params.Pool, []string) map[string]string); ok {
|
||||
r0 = rf(ctx, instance, pool, labels)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
r0 = ret.Get(0).(map[string]string)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, id)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, params.Pool, []string) *github.Runner); ok {
|
||||
r1 = rf(ctx, instance, pool, labels)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
r1 = ret.Get(1).(*github.Runner)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, int64) error); ok {
|
||||
r2 = rf(ctx, owner, repo, id)
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, params.Pool, []string) error); ok {
|
||||
r2 = rf(ctx, instance, pool, labels)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
|
@ -425,38 +223,38 @@ func (_m *GithubClient) GetWorkflowJobByID(ctx context.Context, owner string, re
|
|||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListOrgHooks provides a mock function with given fields: ctx, org, opts
|
||||
func (_m *GithubClient) ListOrgHooks(ctx context.Context, org string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, opts)
|
||||
// ListEntityHooks provides a mock function with given fields: ctx, opts
|
||||
func (_m *GithubClient) ListEntityHooks(ctx context.Context, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, opts)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListOrgHooks")
|
||||
panic("no return value specified for ListEntityHooks")
|
||||
}
|
||||
|
||||
var r0 []*github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) ([]*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, org, opts)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) ([]*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) []*github.Hook); ok {
|
||||
r0 = rf(ctx, org, opts)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) []*github.Hook); ok {
|
||||
r0 = rf(ctx, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, org, opts)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, org, opts)
|
||||
if rf, ok := ret.Get(2).(func(context.Context, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
|
@ -464,38 +262,38 @@ func (_m *GithubClient) ListOrgHooks(ctx context.Context, org string, opts *gith
|
|||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListOrganizationRunnerApplicationDownloads provides a mock function with given fields: ctx, owner
|
||||
func (_m *GithubClient) ListOrganizationRunnerApplicationDownloads(ctx context.Context, owner string) ([]*github.RunnerApplicationDownload, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner)
|
||||
// ListEntityRunnerApplicationDownloads provides a mock function with given fields: ctx
|
||||
func (_m *GithubClient) ListEntityRunnerApplicationDownloads(ctx context.Context) ([]*github.RunnerApplicationDownload, *github.Response, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListOrganizationRunnerApplicationDownloads")
|
||||
panic("no return value specified for ListEntityRunnerApplicationDownloads")
|
||||
}
|
||||
|
||||
var r0 []*github.RunnerApplicationDownload
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) ([]*github.RunnerApplicationDownload, *github.Response, error)); ok {
|
||||
return rf(ctx, owner)
|
||||
if rf, ok := ret.Get(0).(func(context.Context) ([]*github.RunnerApplicationDownload, *github.Response, error)); ok {
|
||||
return rf(ctx)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []*github.RunnerApplicationDownload); ok {
|
||||
r0 = rf(ctx, owner)
|
||||
if rf, ok := ret.Get(0).(func(context.Context) []*github.RunnerApplicationDownload); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.RunnerApplicationDownload)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) *github.Response); ok {
|
||||
r1 = rf(ctx, owner)
|
||||
if rf, ok := ret.Get(1).(func(context.Context) *github.Response); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string) error); ok {
|
||||
r2 = rf(ctx, owner)
|
||||
if rf, ok := ret.Get(2).(func(context.Context) error); ok {
|
||||
r2 = rf(ctx)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
|
@ -503,77 +301,38 @@ func (_m *GithubClient) ListOrganizationRunnerApplicationDownloads(ctx context.C
|
|||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListOrganizationRunnerGroups provides a mock function with given fields: ctx, org, opts
|
||||
func (_m *GithubClient) ListOrganizationRunnerGroups(ctx context.Context, org string, opts *github.ListOrgRunnerGroupOptions) (*github.RunnerGroups, *github.Response, error) {
|
||||
ret := _m.Called(ctx, org, opts)
|
||||
// ListEntityRunners provides a mock function with given fields: ctx, opts
|
||||
func (_m *GithubClient) ListEntityRunners(ctx context.Context, opts *github.ListOptions) (*github.Runners, *github.Response, error) {
|
||||
ret := _m.Called(ctx, opts)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListOrganizationRunnerGroups")
|
||||
}
|
||||
|
||||
var r0 *github.RunnerGroups
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOrgRunnerGroupOptions) (*github.RunnerGroups, *github.Response, error)); ok {
|
||||
return rf(ctx, org, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOrgRunnerGroupOptions) *github.RunnerGroups); ok {
|
||||
r0 = rf(ctx, org, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.RunnerGroups)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOrgRunnerGroupOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, org, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOrgRunnerGroupOptions) error); ok {
|
||||
r2 = rf(ctx, org, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListOrganizationRunners provides a mock function with given fields: ctx, owner, opts
|
||||
func (_m *GithubClient) ListOrganizationRunners(ctx context.Context, owner string, opts *github.ListOptions) (*github.Runners, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, opts)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListOrganizationRunners")
|
||||
panic("no return value specified for ListEntityRunners")
|
||||
}
|
||||
|
||||
var r0 *github.Runners
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) (*github.Runners, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, opts)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) (*github.Runners, *github.Response, error)); ok {
|
||||
return rf(ctx, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) *github.Runners); ok {
|
||||
r0 = rf(ctx, owner, opts)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) *github.Runners); ok {
|
||||
r0 = rf(ctx, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Runners)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, opts)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, owner, opts)
|
||||
if rf, ok := ret.Get(2).(func(context.Context, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
|
@ -581,146 +340,29 @@ func (_m *GithubClient) ListOrganizationRunners(ctx context.Context, owner strin
|
|||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListRepoHooks provides a mock function with given fields: ctx, owner, repo, opts
|
||||
func (_m *GithubClient) ListRepoHooks(ctx context.Context, owner string, repo string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, opts)
|
||||
// PingEntityHook provides a mock function with given fields: ctx, id
|
||||
func (_m *GithubClient) PingEntityHook(ctx context.Context, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListRepoHooks")
|
||||
}
|
||||
|
||||
var r0 []*github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.ListOptions) ([]*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.ListOptions) []*github.Hook); ok {
|
||||
r0 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListRunnerApplicationDownloads provides a mock function with given fields: ctx, owner, repo
|
||||
func (_m *GithubClient) ListRunnerApplicationDownloads(ctx context.Context, owner string, repo string) ([]*github.RunnerApplicationDownload, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListRunnerApplicationDownloads")
|
||||
}
|
||||
|
||||
var r0 []*github.RunnerApplicationDownload
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]*github.RunnerApplicationDownload, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) []*github.RunnerApplicationDownload); ok {
|
||||
r0 = rf(ctx, owner, repo)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.RunnerApplicationDownload)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok {
|
||||
r2 = rf(ctx, owner, repo)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListRunners provides a mock function with given fields: ctx, owner, repo, opts
|
||||
func (_m *GithubClient) ListRunners(ctx context.Context, owner string, repo string, opts *github.ListOptions) (*github.Runners, *github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, opts)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListRunners")
|
||||
}
|
||||
|
||||
var r0 *github.Runners
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.ListOptions) (*github.Runners, *github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.ListOptions) *github.Runners); ok {
|
||||
r0 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Runners)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, owner, repo, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// PingOrgHook provides a mock function with given fields: ctx, org, id
|
||||
func (_m *GithubClient) PingOrgHook(ctx context.Context, org string, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, org, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for PingOrgHook")
|
||||
panic("no return value specified for PingEntityHook")
|
||||
}
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, org, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, org, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok {
|
||||
r1 = rf(ctx, org, id)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
|
@ -728,89 +370,29 @@ func (_m *GithubClient) PingOrgHook(ctx context.Context, org string, id int64) (
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// PingRepoHook provides a mock function with given fields: ctx, owner, repo, id
|
||||
func (_m *GithubClient) PingRepoHook(ctx context.Context, owner string, repo string, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, id)
|
||||
// RemoveEntityRunner provides a mock function with given fields: ctx, runnerID
|
||||
func (_m *GithubClient) RemoveEntityRunner(ctx context.Context, runnerID int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, runnerID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for PingRepoHook")
|
||||
panic("no return value specified for RemoveEntityRunner")
|
||||
}
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, runnerID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, owner, repo, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, runnerID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) error); ok {
|
||||
r1 = rf(ctx, owner, repo, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RemoveOrganizationRunner provides a mock function with given fields: ctx, owner, runnerID
|
||||
func (_m *GithubClient) RemoveOrganizationRunner(ctx context.Context, owner string, runnerID int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, runnerID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RemoveOrganizationRunner")
|
||||
}
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, owner, runnerID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, owner, runnerID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok {
|
||||
r1 = rf(ctx, owner, runnerID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RemoveRunner provides a mock function with given fields: ctx, owner, repo, runnerID
|
||||
func (_m *GithubClient) RemoveRunner(ctx context.Context, owner string, repo string, runnerID int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, owner, repo, runnerID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RemoveRunner")
|
||||
}
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, owner, repo, runnerID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, owner, repo, runnerID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) error); ok {
|
||||
r1 = rf(ctx, owner, repo, runnerID)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, runnerID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
|
|
|||
376
runner/common/mocks/GithubEntityOperations.go
Normal file
376
runner/common/mocks/GithubEntityOperations.go
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
// Code generated by mockery v2.42.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
github "github.com/google/go-github/v57/github"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
params "github.com/cloudbase/garm/params"
|
||||
)
|
||||
|
||||
// GithubEntityOperations is an autogenerated mock type for the GithubEntityOperations type
|
||||
type GithubEntityOperations struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CreateEntityHook provides a mock function with given fields: ctx, hook
|
||||
func (_m *GithubEntityOperations) CreateEntityHook(ctx context.Context, hook *github.Hook) (*github.Hook, error) {
|
||||
ret := _m.Called(ctx, hook)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateEntityHook")
|
||||
}
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.Hook) (*github.Hook, error)); ok {
|
||||
return rf(ctx, hook)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.Hook) *github.Hook); ok {
|
||||
r0 = rf(ctx, hook)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *github.Hook) error); ok {
|
||||
r1 = rf(ctx, hook)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateEntityRegistrationToken provides a mock function with given fields: ctx
|
||||
func (_m *GithubEntityOperations) CreateEntityRegistrationToken(ctx context.Context) (*github.RegistrationToken, *github.Response, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateEntityRegistrationToken")
|
||||
}
|
||||
|
||||
var r0 *github.RegistrationToken
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) (*github.RegistrationToken, *github.Response, error)); ok {
|
||||
return rf(ctx)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context) *github.RegistrationToken); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.RegistrationToken)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context) *github.Response); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context) error); ok {
|
||||
r2 = rf(ctx)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// DeleteEntityHook provides a mock function with given fields: ctx, id
|
||||
func (_m *GithubEntityOperations) DeleteEntityHook(ctx context.Context, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteEntityHook")
|
||||
}
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetEntityHook provides a mock function with given fields: ctx, id
|
||||
func (_m *GithubEntityOperations) GetEntityHook(ctx context.Context, id int64) (*github.Hook, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetEntityHook")
|
||||
}
|
||||
|
||||
var r0 *github.Hook
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*github.Hook, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *github.Hook); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetEntityJITConfig provides a mock function with given fields: ctx, instance, pool, labels
|
||||
func (_m *GithubEntityOperations) GetEntityJITConfig(ctx context.Context, instance string, pool params.Pool, labels []string) (map[string]string, *github.Runner, error) {
|
||||
ret := _m.Called(ctx, instance, pool, labels)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetEntityJITConfig")
|
||||
}
|
||||
|
||||
var r0 map[string]string
|
||||
var r1 *github.Runner
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, params.Pool, []string) (map[string]string, *github.Runner, error)); ok {
|
||||
return rf(ctx, instance, pool, labels)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, params.Pool, []string) map[string]string); ok {
|
||||
r0 = rf(ctx, instance, pool, labels)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(map[string]string)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, params.Pool, []string) *github.Runner); ok {
|
||||
r1 = rf(ctx, instance, pool, labels)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Runner)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, params.Pool, []string) error); ok {
|
||||
r2 = rf(ctx, instance, pool, labels)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListEntityHooks provides a mock function with given fields: ctx, opts
|
||||
func (_m *GithubEntityOperations) ListEntityHooks(ctx context.Context, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
ret := _m.Called(ctx, opts)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListEntityHooks")
|
||||
}
|
||||
|
||||
var r0 []*github.Hook
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) ([]*github.Hook, *github.Response, error)); ok {
|
||||
return rf(ctx, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) []*github.Hook); ok {
|
||||
r0 = rf(ctx, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListEntityRunnerApplicationDownloads provides a mock function with given fields: ctx
|
||||
func (_m *GithubEntityOperations) ListEntityRunnerApplicationDownloads(ctx context.Context) ([]*github.RunnerApplicationDownload, *github.Response, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListEntityRunnerApplicationDownloads")
|
||||
}
|
||||
|
||||
var r0 []*github.RunnerApplicationDownload
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) ([]*github.RunnerApplicationDownload, *github.Response, error)); ok {
|
||||
return rf(ctx)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context) []*github.RunnerApplicationDownload); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*github.RunnerApplicationDownload)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context) *github.Response); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context) error); ok {
|
||||
r2 = rf(ctx)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// ListEntityRunners provides a mock function with given fields: ctx, opts
|
||||
func (_m *GithubEntityOperations) ListEntityRunners(ctx context.Context, opts *github.ListOptions) (*github.Runners, *github.Response, error) {
|
||||
ret := _m.Called(ctx, opts)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListEntityRunners")
|
||||
}
|
||||
|
||||
var r0 *github.Runners
|
||||
var r1 *github.Response
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) (*github.Runners, *github.Response, error)); ok {
|
||||
return rf(ctx, opts)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) *github.Runners); ok {
|
||||
r0 = rf(ctx, opts)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Runners)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *github.ListOptions) *github.Response); ok {
|
||||
r1 = rf(ctx, opts)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, *github.ListOptions) error); ok {
|
||||
r2 = rf(ctx, opts)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// PingEntityHook provides a mock function with given fields: ctx, id
|
||||
func (_m *GithubEntityOperations) PingEntityHook(ctx context.Context, id int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for PingEntityHook")
|
||||
}
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RemoveEntityRunner provides a mock function with given fields: ctx, runnerID
|
||||
func (_m *GithubEntityOperations) RemoveEntityRunner(ctx context.Context, runnerID int64) (*github.Response, error) {
|
||||
ret := _m.Called(ctx, runnerID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RemoveEntityRunner")
|
||||
}
|
||||
|
||||
var r0 *github.Response
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*github.Response, error)); ok {
|
||||
return rf(ctx, runnerID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *github.Response); ok {
|
||||
r0 = rf(ctx, runnerID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*github.Response)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, runnerID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewGithubEntityOperations creates a new instance of GithubEntityOperations. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewGithubEntityOperations(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *GithubEntityOperations {
|
||||
mock := &GithubEntityOperations{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
|
@ -4,22 +4,21 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
|
||||
"github.com/cloudbase/garm/params"
|
||||
)
|
||||
|
||||
type OrganizationHooks interface {
|
||||
ListOrgHooks(ctx context.Context, org string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error)
|
||||
GetOrgHook(ctx context.Context, org string, id int64) (*github.Hook, *github.Response, error)
|
||||
CreateOrgHook(ctx context.Context, org string, hook *github.Hook) (*github.Hook, *github.Response, error)
|
||||
DeleteOrgHook(ctx context.Context, org string, id int64) (*github.Response, error)
|
||||
PingOrgHook(ctx context.Context, org string, id int64) (*github.Response, error)
|
||||
}
|
||||
|
||||
type RepositoryHooks interface {
|
||||
ListRepoHooks(ctx context.Context, owner, repo string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error)
|
||||
GetRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Hook, *github.Response, error)
|
||||
CreateRepoHook(ctx context.Context, owner, repo string, hook *github.Hook) (*github.Hook, *github.Response, error)
|
||||
DeleteRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Response, error)
|
||||
PingRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Response, error)
|
||||
type GithubEntityOperations interface {
|
||||
ListEntityHooks(ctx context.Context, opts *github.ListOptions) (ret []*github.Hook, response *github.Response, err error)
|
||||
GetEntityHook(ctx context.Context, id int64) (ret *github.Hook, err error)
|
||||
CreateEntityHook(ctx context.Context, hook *github.Hook) (ret *github.Hook, err error)
|
||||
DeleteEntityHook(ctx context.Context, id int64) (ret *github.Response, err error)
|
||||
PingEntityHook(ctx context.Context, id int64) (ret *github.Response, err error)
|
||||
ListEntityRunners(ctx context.Context, opts *github.ListOptions) (*github.Runners, *github.Response, error)
|
||||
ListEntityRunnerApplicationDownloads(ctx context.Context) ([]*github.RunnerApplicationDownload, *github.Response, error)
|
||||
RemoveEntityRunner(ctx context.Context, runnerID int64) (*github.Response, error)
|
||||
CreateEntityRegistrationToken(ctx context.Context) (*github.RegistrationToken, *github.Response, error)
|
||||
GetEntityJITConfig(ctx context.Context, instance string, pool params.Pool, labels []string) (jitConfigMap map[string]string, runner *github.Runner, err error)
|
||||
}
|
||||
|
||||
// GithubClient that describes the minimum list of functions we need to interact with github.
|
||||
|
|
@ -27,50 +26,8 @@ type RepositoryHooks interface {
|
|||
//
|
||||
//go:generate mockery --all
|
||||
type GithubClient interface {
|
||||
OrganizationHooks
|
||||
RepositoryHooks
|
||||
GithubEntityOperations
|
||||
|
||||
// GetWorkflowJobByID gets details about a single workflow job.
|
||||
GetWorkflowJobByID(ctx context.Context, owner, repo string, jobID int64) (*github.WorkflowJob, *github.Response, error)
|
||||
// ListRunners lists all runners within a repository.
|
||||
ListRunners(ctx context.Context, owner, repo string, opts *github.ListOptions) (*github.Runners, *github.Response, error)
|
||||
// ListRunnerApplicationDownloads returns a list of github runner application downloads for the
|
||||
// various supported operating systems and architectures.
|
||||
ListRunnerApplicationDownloads(ctx context.Context, owner, repo string) ([]*github.RunnerApplicationDownload, *github.Response, error)
|
||||
// RemoveRunner removes one runner from a repository.
|
||||
RemoveRunner(ctx context.Context, owner, repo string, runnerID int64) (*github.Response, error)
|
||||
// CreateRegistrationToken creates a runner registration token for one repository.
|
||||
CreateRegistrationToken(ctx context.Context, owner, repo string) (*github.RegistrationToken, *github.Response, error)
|
||||
// GenerateRepoJITConfig generates a just-in-time configuration for a repository.
|
||||
GenerateRepoJITConfig(ctx context.Context, owner, repo string, request *github.GenerateJITConfigRequest) (*github.JITRunnerConfig, *github.Response, error)
|
||||
|
||||
// ListOrganizationRunners lists all runners within an organization.
|
||||
ListOrganizationRunners(ctx context.Context, owner string, opts *github.ListOptions) (*github.Runners, *github.Response, error)
|
||||
// ListOrganizationRunnerApplicationDownloads returns a list of github runner application downloads for the
|
||||
// various supported operating systems and architectures.
|
||||
ListOrganizationRunnerApplicationDownloads(ctx context.Context, owner string) ([]*github.RunnerApplicationDownload, *github.Response, error)
|
||||
// RemoveOrganizationRunner removes one github runner from an organization.
|
||||
RemoveOrganizationRunner(ctx context.Context, owner string, runnerID int64) (*github.Response, error)
|
||||
// CreateOrganizationRegistrationToken creates a runner registration token for an organization.
|
||||
CreateOrganizationRegistrationToken(ctx context.Context, owner string) (*github.RegistrationToken, *github.Response, error)
|
||||
// GenerateOrgJITConfig generate a just-in-time configuration for an organization.
|
||||
GenerateOrgJITConfig(ctx context.Context, owner string, request *github.GenerateJITConfigRequest) (*github.JITRunnerConfig, *github.Response, error)
|
||||
// ListOrganizationRunnerGroups lists all runner groups within an organization.
|
||||
ListOrganizationRunnerGroups(ctx context.Context, org string, opts *github.ListOrgRunnerGroupOptions) (*github.RunnerGroups, *github.Response, error)
|
||||
}
|
||||
|
||||
type GithubEnterpriseClient interface {
|
||||
// ListRunners lists all runners within a repository.
|
||||
ListRunners(ctx context.Context, enterprise string, opts *github.ListOptions) (*github.Runners, *github.Response, error)
|
||||
// RemoveRunner removes one runner from an enterprise.
|
||||
RemoveRunner(ctx context.Context, enterprise string, runnerID int64) (*github.Response, error)
|
||||
// CreateRegistrationToken creates a runner registration token for an enterprise.
|
||||
CreateRegistrationToken(ctx context.Context, enterprise string) (*github.RegistrationToken, *github.Response, error)
|
||||
// ListRunnerApplicationDownloads returns a list of github runner application downloads for the
|
||||
// various supported operating systems and architectures.
|
||||
ListRunnerApplicationDownloads(ctx context.Context, enterprise string) ([]*github.RunnerApplicationDownload, *github.Response, error)
|
||||
// GenerateEnterpriseJITConfig generate a just-in-time configuration for an enterprise.
|
||||
GenerateEnterpriseJITConfig(ctx context.Context, enterprise string, request *github.GenerateJITConfigRequest) (*github.JITRunnerConfig, *github.Response, error)
|
||||
// ListRunnerGroups lists all self-hosted runner groups configured in an enterprise.
|
||||
ListRunnerGroups(ctx context.Context, enterprise string, opts *github.ListEnterpriseRunnerGroupOptions) (*github.EnterpriseRunnerGroups, *github.Response, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,11 +69,11 @@ func (r *Runner) GetRunnerServiceName(ctx context.Context) (string, error) {
|
|||
tpl := "actions.runner.%s.%s"
|
||||
var serviceName string
|
||||
switch pool.PoolType() {
|
||||
case params.EnterprisePool:
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
serviceName = fmt.Sprintf(tpl, pool.EnterpriseName, instance.Name)
|
||||
case params.OrganizationPool:
|
||||
case params.GithubEntityTypeOrganization:
|
||||
serviceName = fmt.Sprintf(tpl, pool.OrgName, instance.Name)
|
||||
case params.RepositoryPool:
|
||||
case params.GithubEntityTypeRepository:
|
||||
serviceName = fmt.Sprintf(tpl, strings.ReplaceAll(pool.RepoName, "/", "-"), instance.Name)
|
||||
}
|
||||
return serviceName, nil
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package pool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
|
|
@ -62,3 +64,25 @@ func hookToParamsHookInfo(hook *github.Hook) params.HookInfo {
|
|||
InsecureSSL: insecureSSL,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *basePoolManager) listHooks(ctx context.Context) ([]*github.Hook, error) {
|
||||
opts := github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
var allHooks []*github.Hook
|
||||
for {
|
||||
hooks, ghResp, err := r.ghcli.ListEntityHooks(ctx, &opts)
|
||||
if err != nil {
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusNotFound {
|
||||
return nil, runnerErrors.NewBadRequestError("repository not found or your PAT does not have access to manage webhooks")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching hooks")
|
||||
}
|
||||
allHooks = append(allHooks, hooks...)
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
return allHooks, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,398 +0,0 @@
|
|||
package pool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
runnerErrors "github.com/cloudbase/garm-provider-common/errors"
|
||||
commonParams "github.com/cloudbase/garm-provider-common/params"
|
||||
dbCommon "github.com/cloudbase/garm/database/common"
|
||||
"github.com/cloudbase/garm/metrics"
|
||||
"github.com/cloudbase/garm/params"
|
||||
"github.com/cloudbase/garm/runner/common"
|
||||
"github.com/cloudbase/garm/util"
|
||||
)
|
||||
|
||||
// test that we implement PoolManager
|
||||
var _ poolHelper = &enterprise{}
|
||||
|
||||
func NewEnterprisePoolManager(ctx context.Context, cfg params.Enterprise, cfgInternal params.Internal, providers map[string]common.Provider, store dbCommon.Store) (common.PoolManager, error) {
|
||||
ctx = util.WithContext(ctx, slog.Any("pool_mgr", cfg.Name), slog.Any("pool_type", params.EnterprisePool))
|
||||
ghc, ghEnterpriseClient, err := util.GithubClient(ctx, cfgInternal.GithubCredentialsDetails)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getting github client")
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
keyMuxes := &keyMutex{}
|
||||
|
||||
helper := &enterprise{
|
||||
cfg: cfg,
|
||||
cfgInternal: cfgInternal,
|
||||
ctx: ctx,
|
||||
ghcli: ghc,
|
||||
ghcEnterpriseCli: ghEnterpriseClient,
|
||||
id: cfg.ID,
|
||||
store: store,
|
||||
}
|
||||
|
||||
repo := &basePoolManager{
|
||||
ctx: ctx,
|
||||
store: store,
|
||||
providers: providers,
|
||||
controllerID: cfgInternal.ControllerID,
|
||||
urls: urls{
|
||||
webhookURL: cfgInternal.BaseWebhookURL,
|
||||
callbackURL: cfgInternal.InstanceCallbackURL,
|
||||
metadataURL: cfgInternal.InstanceMetadataURL,
|
||||
controllerWebhookURL: cfgInternal.ControllerWebhookURL,
|
||||
},
|
||||
quit: make(chan struct{}),
|
||||
helper: helper,
|
||||
credsDetails: cfgInternal.GithubCredentialsDetails,
|
||||
wg: wg,
|
||||
keyMux: keyMuxes,
|
||||
}
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
type enterprise struct {
|
||||
cfg params.Enterprise
|
||||
cfgInternal params.Internal
|
||||
ctx context.Context
|
||||
ghcli common.GithubClient
|
||||
ghcEnterpriseCli common.GithubEnterpriseClient
|
||||
id string
|
||||
store dbCommon.Store
|
||||
|
||||
mux sync.Mutex
|
||||
}
|
||||
|
||||
func (e *enterprise) PoolBalancerType() params.PoolBalancerType {
|
||||
if e.cfgInternal.PoolBalancerType == "" {
|
||||
return params.PoolBalancerTypeRoundRobin
|
||||
}
|
||||
return e.cfgInternal.PoolBalancerType
|
||||
}
|
||||
|
||||
func (e *enterprise) findRunnerGroupByName(name string) (*github.EnterpriseRunnerGroup, error) {
|
||||
// nolint:golangci-lint,godox
|
||||
// TODO(gabriel-samfira): implement caching
|
||||
opts := github.ListEnterpriseRunnerGroupOptions{
|
||||
ListOptions: github.ListOptions{
|
||||
PerPage: 100,
|
||||
},
|
||||
}
|
||||
|
||||
for {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListOrganizationRunnerGroups", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
runnerGroups, ghResp, err := e.ghcEnterpriseCli.ListRunnerGroups(e.ctx, e.cfg.Name, &opts)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListOrganizationRunnerGroups", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runners")
|
||||
}
|
||||
for _, runnerGroup := range runnerGroups.RunnerGroups {
|
||||
if runnerGroup.Name != nil && *runnerGroup.Name == name {
|
||||
return runnerGroup, nil
|
||||
}
|
||||
}
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
|
||||
return nil, errors.Wrap(runnerErrors.ErrNotFound, "runner group not found")
|
||||
}
|
||||
|
||||
func (e *enterprise) GetJITConfig(ctx context.Context, instance string, pool params.Pool, labels []string) (jitConfigMap map[string]string, runner *github.Runner, err error) {
|
||||
var rg int64 = 1
|
||||
if pool.GitHubRunnerGroup != "" {
|
||||
runnerGroup, err := e.findRunnerGroupByName(pool.GitHubRunnerGroup)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to find runner group: %w", err)
|
||||
}
|
||||
rg = *runnerGroup.ID
|
||||
}
|
||||
|
||||
req := github.GenerateJITConfigRequest{
|
||||
Name: instance,
|
||||
RunnerGroupID: rg,
|
||||
Labels: labels,
|
||||
// nolint:golangci-lint,godox
|
||||
// TODO(gabriel-samfira): Should we make this configurable?
|
||||
WorkFolder: github.String("_work"),
|
||||
}
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"GenerateEnterpriseJITConfig", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
jitConfig, resp, err := e.ghcEnterpriseCli.GenerateEnterpriseJITConfig(ctx, e.cfg.Name, &req)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"GenerateEnterpriseJITConfig", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
if resp != nil && resp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, nil, fmt.Errorf("failed to get JIT config: %w", err)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to get JIT config: %w", err)
|
||||
}
|
||||
|
||||
runner = jitConfig.Runner
|
||||
defer func() {
|
||||
if err != nil && runner != nil {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
_, innerErr := e.ghcEnterpriseCli.RemoveRunner(e.ctx, e.cfg.Name, runner.GetID())
|
||||
if innerErr != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
}
|
||||
slog.With(slog.Any("error", innerErr)).ErrorContext(
|
||||
ctx, "failed to remove runner",
|
||||
"runner_id", runner.GetID(), "organization", e.cfg.Name)
|
||||
}
|
||||
}()
|
||||
|
||||
decoded, err := base64.StdEncoding.DecodeString(*jitConfig.EncodedJITConfig)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode JIT config: %w", err)
|
||||
}
|
||||
|
||||
var ret map[string]string
|
||||
if err := json.Unmarshal(decoded, &ret); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal JIT config: %w", err)
|
||||
}
|
||||
|
||||
return ret, jitConfig.Runner, nil
|
||||
}
|
||||
|
||||
func (e *enterprise) PoolType() params.PoolType {
|
||||
return params.EnterprisePool
|
||||
}
|
||||
|
||||
func (e *enterprise) GetRunnerInfoFromWorkflow(job params.WorkflowJob) (params.RunnerInfo, error) {
|
||||
if err := e.ValidateOwner(job); err != nil {
|
||||
return params.RunnerInfo{}, errors.Wrap(err, "validating owner")
|
||||
}
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"GetWorkflowJobByID", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
workflow, ghResp, err := e.ghcli.GetWorkflowJobByID(e.ctx, job.Repository.Owner.Login, job.Repository.Name, job.WorkflowJob.ID)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"GetWorkflowJobByID", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return params.RunnerInfo{}, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching workflow info")
|
||||
}
|
||||
return params.RunnerInfo{}, errors.Wrap(err, "fetching workflow info")
|
||||
}
|
||||
|
||||
if workflow.RunnerName != nil {
|
||||
return params.RunnerInfo{
|
||||
Name: *workflow.RunnerName,
|
||||
Labels: workflow.Labels,
|
||||
}, nil
|
||||
}
|
||||
return params.RunnerInfo{}, fmt.Errorf("failed to find runner name from workflow")
|
||||
}
|
||||
|
||||
func (e *enterprise) UpdateState(param params.UpdatePoolStateParams) error {
|
||||
e.mux.Lock()
|
||||
defer e.mux.Unlock()
|
||||
|
||||
e.cfg.WebhookSecret = param.WebhookSecret
|
||||
if param.InternalConfig != nil {
|
||||
e.cfgInternal = *param.InternalConfig
|
||||
}
|
||||
|
||||
ghc, ghcEnterprise, err := util.GithubClient(e.ctx, e.cfgInternal.GithubCredentialsDetails)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting github client")
|
||||
}
|
||||
e.ghcli = ghc
|
||||
e.ghcEnterpriseCli = ghcEnterprise
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *enterprise) GetGithubRunners() ([]*github.Runner, error) {
|
||||
opts := github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
|
||||
var allRunners []*github.Runner
|
||||
for {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListRunners", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
runners, ghResp, err := e.ghcEnterpriseCli.ListRunners(e.ctx, e.cfg.Name, &opts)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListRunners", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runners")
|
||||
}
|
||||
allRunners = append(allRunners, runners.Runners...)
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
return allRunners, nil
|
||||
}
|
||||
|
||||
func (e *enterprise) FetchTools() ([]commonParams.RunnerApplicationDownload, error) {
|
||||
e.mux.Lock()
|
||||
defer e.mux.Unlock()
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListRunnerApplicationDownloads", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
tools, ghResp, err := e.ghcEnterpriseCli.ListRunnerApplicationDownloads(e.ctx, e.cfg.Name)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListRunnerApplicationDownloads", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runner tools")
|
||||
}
|
||||
|
||||
ret := []commonParams.RunnerApplicationDownload{}
|
||||
for _, tool := range tools {
|
||||
if tool == nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, commonParams.RunnerApplicationDownload(*tool))
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (e *enterprise) FetchDbInstances() ([]params.Instance, error) {
|
||||
return e.store.ListEnterpriseInstances(e.ctx, e.id)
|
||||
}
|
||||
|
||||
func (e *enterprise) RemoveGithubRunner(runnerID int64) (*github.Response, error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
ghResp, err := e.ghcEnterpriseCli.RemoveRunner(e.ctx, e.cfg.Name, runnerID)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
return nil, err
|
||||
}
|
||||
return ghResp, nil
|
||||
}
|
||||
|
||||
func (e *enterprise) ListPools() ([]params.Pool, error) {
|
||||
pools, err := e.store.ListEnterprisePools(e.ctx, e.id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching pools")
|
||||
}
|
||||
return pools, nil
|
||||
}
|
||||
|
||||
func (e *enterprise) GithubURL() string {
|
||||
return fmt.Sprintf("%s/enterprises/%s", e.cfgInternal.GithubCredentialsDetails.BaseURL, e.cfg.Name)
|
||||
}
|
||||
|
||||
func (e *enterprise) JwtToken() string {
|
||||
return e.cfgInternal.JWTSecret
|
||||
}
|
||||
|
||||
func (e *enterprise) GetGithubRegistrationToken() (string, error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"CreateRegistrationToken", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
|
||||
tk, ghResp, err := e.ghcEnterpriseCli.CreateRegistrationToken(e.ctx, e.cfg.Name)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"CreateRegistrationToken", // label: operation
|
||||
metricsLabelEnterpriseScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return "", errors.Wrap(runnerErrors.ErrUnauthorized, "fetching registration token")
|
||||
}
|
||||
return "", errors.Wrap(err, "creating runner token")
|
||||
}
|
||||
return *tk.Token, nil
|
||||
}
|
||||
|
||||
func (e *enterprise) String() string {
|
||||
return e.cfg.Name
|
||||
}
|
||||
|
||||
func (e *enterprise) WebhookSecret() string {
|
||||
return e.cfg.WebhookSecret
|
||||
}
|
||||
|
||||
func (e *enterprise) GetPoolByID(poolID string) (params.Pool, error) {
|
||||
pool, err := e.store.GetEnterprisePool(e.ctx, e.id, poolID)
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
func (e *enterprise) ValidateOwner(job params.WorkflowJob) error {
|
||||
if !strings.EqualFold(job.Enterprise.Slug, e.cfg.Name) {
|
||||
return runnerErrors.NewBadRequestError("job not meant for this pool manager")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *enterprise) ID() string {
|
||||
return e.id
|
||||
}
|
||||
|
||||
func (e *enterprise) InstallHook(_ context.Context, _ *github.Hook) (params.HookInfo, error) {
|
||||
return params.HookInfo{}, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (e *enterprise) UninstallHook(_ context.Context, _ string) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (e *enterprise) GetHookInfo(_ context.Context) (params.HookInfo, error) {
|
||||
return params.HookInfo{}, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
// 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.
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
|
||||
commonParams "github.com/cloudbase/garm-provider-common/params"
|
||||
"github.com/cloudbase/garm/params"
|
||||
)
|
||||
|
||||
type poolHelper interface {
|
||||
GetGithubRunners() ([]*github.Runner, error)
|
||||
GetGithubRegistrationToken() (string, error)
|
||||
GetRunnerInfoFromWorkflow(job params.WorkflowJob) (params.RunnerInfo, error)
|
||||
RemoveGithubRunner(runnerID int64) (*github.Response, error)
|
||||
FetchTools() ([]commonParams.RunnerApplicationDownload, error)
|
||||
|
||||
InstallHook(ctx context.Context, req *github.Hook) (params.HookInfo, error)
|
||||
UninstallHook(ctx context.Context, url string) error
|
||||
GetHookInfo(ctx context.Context) (params.HookInfo, error)
|
||||
|
||||
GetJITConfig(ctx context.Context, instanceName string, pool params.Pool, labels []string) (map[string]string, *github.Runner, error)
|
||||
|
||||
FetchDbInstances() ([]params.Instance, error)
|
||||
ListPools() ([]params.Pool, error)
|
||||
GithubURL() string
|
||||
JwtToken() string
|
||||
String() string
|
||||
GetPoolByID(poolID string) (params.Pool, error)
|
||||
ValidateOwner(job params.WorkflowJob) error
|
||||
UpdateState(param params.UpdatePoolStateParams) error
|
||||
WebhookSecret() string
|
||||
ID() string
|
||||
PoolType() params.PoolType
|
||||
PoolBalancerType() params.PoolBalancerType
|
||||
}
|
||||
|
|
@ -1,512 +0,0 @@
|
|||
// 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.
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
runnerErrors "github.com/cloudbase/garm-provider-common/errors"
|
||||
commonParams "github.com/cloudbase/garm-provider-common/params"
|
||||
dbCommon "github.com/cloudbase/garm/database/common"
|
||||
"github.com/cloudbase/garm/metrics"
|
||||
"github.com/cloudbase/garm/params"
|
||||
"github.com/cloudbase/garm/runner/common"
|
||||
"github.com/cloudbase/garm/util"
|
||||
)
|
||||
|
||||
// test that we implement PoolManager
|
||||
var _ poolHelper = &organization{}
|
||||
|
||||
func NewOrganizationPoolManager(ctx context.Context, cfg params.Organization, cfgInternal params.Internal, providers map[string]common.Provider, store dbCommon.Store) (common.PoolManager, error) {
|
||||
ctx = util.WithContext(ctx, slog.Any("pool_mgr", cfg.Name), slog.Any("pool_type", params.OrganizationPool))
|
||||
ghc, _, err := util.GithubClient(ctx, cfgInternal.GithubCredentialsDetails)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getting github client")
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
keyMuxes := &keyMutex{}
|
||||
|
||||
helper := &organization{
|
||||
cfg: cfg,
|
||||
cfgInternal: cfgInternal,
|
||||
ctx: ctx,
|
||||
ghcli: ghc,
|
||||
id: cfg.ID,
|
||||
store: store,
|
||||
}
|
||||
|
||||
repo := &basePoolManager{
|
||||
ctx: ctx,
|
||||
store: store,
|
||||
providers: providers,
|
||||
controllerID: cfgInternal.ControllerID,
|
||||
urls: urls{
|
||||
webhookURL: cfgInternal.BaseWebhookURL,
|
||||
callbackURL: cfgInternal.InstanceCallbackURL,
|
||||
metadataURL: cfgInternal.InstanceMetadataURL,
|
||||
controllerWebhookURL: cfgInternal.ControllerWebhookURL,
|
||||
},
|
||||
quit: make(chan struct{}),
|
||||
helper: helper,
|
||||
credsDetails: cfgInternal.GithubCredentialsDetails,
|
||||
wg: wg,
|
||||
keyMux: keyMuxes,
|
||||
}
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
type organization struct {
|
||||
cfg params.Organization
|
||||
cfgInternal params.Internal
|
||||
ctx context.Context
|
||||
ghcli common.GithubClient
|
||||
id string
|
||||
store dbCommon.Store
|
||||
|
||||
mux sync.Mutex
|
||||
}
|
||||
|
||||
func (o *organization) PoolBalancerType() params.PoolBalancerType {
|
||||
if o.cfgInternal.PoolBalancerType == "" {
|
||||
return params.PoolBalancerTypeRoundRobin
|
||||
}
|
||||
return o.cfgInternal.PoolBalancerType
|
||||
}
|
||||
|
||||
func (o *organization) findRunnerGroupByName(name string) (*github.RunnerGroup, error) {
|
||||
// nolint:golangci-lint,godox
|
||||
// TODO(gabriel-samfira): implement caching
|
||||
opts := github.ListOrgRunnerGroupOptions{
|
||||
ListOptions: github.ListOptions{
|
||||
PerPage: 100,
|
||||
},
|
||||
}
|
||||
|
||||
for {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListOrganizationRunnerGroups", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
runnerGroups, ghResp, err := o.ghcli.ListOrganizationRunnerGroups(o.ctx, o.cfg.Name, &opts)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListOrganizationRunnerGroups", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runners")
|
||||
}
|
||||
for _, runnerGroup := range runnerGroups.RunnerGroups {
|
||||
if runnerGroup.GetName() == name {
|
||||
return runnerGroup, nil
|
||||
}
|
||||
}
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
|
||||
return nil, errors.Wrap(runnerErrors.ErrNotFound, "runner group not found")
|
||||
}
|
||||
|
||||
func (o *organization) GetJITConfig(ctx context.Context, instance string, pool params.Pool, labels []string) (jitConfigMap map[string]string, runner *github.Runner, err error) {
|
||||
var rg int64 = 1
|
||||
if pool.GitHubRunnerGroup != "" {
|
||||
runnerGroup, err := o.findRunnerGroupByName(pool.GitHubRunnerGroup)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to find runner group: %w", err)
|
||||
}
|
||||
rg = runnerGroup.GetID()
|
||||
}
|
||||
|
||||
req := github.GenerateJITConfigRequest{
|
||||
Name: instance,
|
||||
RunnerGroupID: rg,
|
||||
Labels: labels,
|
||||
// nolint:golangci-lint,godox
|
||||
// TODO(gabriel-samfira): Should we make this configurable?
|
||||
WorkFolder: github.String("_work"),
|
||||
}
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"GenerateOrgJITConfig", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
jitConfig, resp, err := o.ghcli.GenerateOrgJITConfig(ctx, o.cfg.Name, &req)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"GenerateOrgJITConfig", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
if resp != nil && resp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, nil, fmt.Errorf("failed to get JIT config: %w", err)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to get JIT config: %w", err)
|
||||
}
|
||||
|
||||
runner = jitConfig.GetRunner()
|
||||
defer func() {
|
||||
if err != nil && runner != nil {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"RemoveOrganizationRunner", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
_, innerErr := o.ghcli.RemoveOrganizationRunner(o.ctx, o.cfg.Name, runner.GetID())
|
||||
if innerErr != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"RemoveOrganizationRunner", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
}
|
||||
slog.With(slog.Any("error", innerErr)).ErrorContext(
|
||||
ctx, "failed to remove runner",
|
||||
"runner_id", runner.GetID(), "organization", o.cfg.Name)
|
||||
}
|
||||
}()
|
||||
|
||||
decoded, err := base64.StdEncoding.DecodeString(jitConfig.GetEncodedJITConfig())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode JIT config: %w", err)
|
||||
}
|
||||
|
||||
var ret map[string]string
|
||||
if err := json.Unmarshal(decoded, &ret); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal JIT config: %w", err)
|
||||
}
|
||||
|
||||
return ret, runner, nil
|
||||
}
|
||||
|
||||
func (o *organization) PoolType() params.PoolType {
|
||||
return params.OrganizationPool
|
||||
}
|
||||
|
||||
func (o *organization) GetRunnerInfoFromWorkflow(job params.WorkflowJob) (params.RunnerInfo, error) {
|
||||
if err := o.ValidateOwner(job); err != nil {
|
||||
return params.RunnerInfo{}, errors.Wrap(err, "validating owner")
|
||||
}
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"GetWorkflowJobByID", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
workflow, ghResp, err := o.ghcli.GetWorkflowJobByID(o.ctx, job.Organization.Login, job.Repository.Name, job.WorkflowJob.ID)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"GetWorkflowJobByID", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return params.RunnerInfo{}, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching workflow info")
|
||||
}
|
||||
return params.RunnerInfo{}, errors.Wrap(err, "fetching workflow info")
|
||||
}
|
||||
|
||||
if workflow.RunnerName != nil {
|
||||
return params.RunnerInfo{
|
||||
Name: *workflow.RunnerName,
|
||||
Labels: workflow.Labels,
|
||||
}, nil
|
||||
}
|
||||
return params.RunnerInfo{}, fmt.Errorf("failed to find runner name from workflow")
|
||||
}
|
||||
|
||||
func (o *organization) UpdateState(param params.UpdatePoolStateParams) error {
|
||||
o.mux.Lock()
|
||||
defer o.mux.Unlock()
|
||||
|
||||
o.cfg.WebhookSecret = param.WebhookSecret
|
||||
if param.InternalConfig != nil {
|
||||
o.cfgInternal = *param.InternalConfig
|
||||
}
|
||||
|
||||
ghc, _, err := util.GithubClient(o.ctx, o.cfgInternal.GithubCredentialsDetails)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting github client")
|
||||
}
|
||||
o.ghcli = ghc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *organization) GetGithubRunners() ([]*github.Runner, error) {
|
||||
opts := github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
|
||||
var allRunners []*github.Runner
|
||||
for {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListOrganizationRunners", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
runners, ghResp, err := o.ghcli.ListOrganizationRunners(o.ctx, o.cfg.Name, &opts)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListOrganizationRunners", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runners")
|
||||
}
|
||||
allRunners = append(allRunners, runners.Runners...)
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
|
||||
return allRunners, nil
|
||||
}
|
||||
|
||||
func (o *organization) FetchTools() ([]commonParams.RunnerApplicationDownload, error) {
|
||||
o.mux.Lock()
|
||||
defer o.mux.Unlock()
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListOrganizationRunnerApplicationDownloads", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
tools, ghResp, err := o.ghcli.ListOrganizationRunnerApplicationDownloads(o.ctx, o.cfg.Name)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListOrganizationRunnerApplicationDownloads", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching tools")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runner tools")
|
||||
}
|
||||
|
||||
ret := []commonParams.RunnerApplicationDownload{}
|
||||
for _, tool := range tools {
|
||||
if tool == nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, commonParams.RunnerApplicationDownload(*tool))
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (o *organization) FetchDbInstances() ([]params.Instance, error) {
|
||||
return o.store.ListOrgInstances(o.ctx, o.id)
|
||||
}
|
||||
|
||||
func (o *organization) RemoveGithubRunner(runnerID int64) (*github.Response, error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
|
||||
ghResp, err := o.ghcli.RemoveOrganizationRunner(o.ctx, o.cfg.Name, runnerID)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ghResp, nil
|
||||
}
|
||||
|
||||
func (o *organization) ListPools() ([]params.Pool, error) {
|
||||
pools, err := o.store.ListOrgPools(o.ctx, o.id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching pools")
|
||||
}
|
||||
return pools, nil
|
||||
}
|
||||
|
||||
func (o *organization) GithubURL() string {
|
||||
return fmt.Sprintf("%s/%s", o.cfgInternal.GithubCredentialsDetails.BaseURL, o.cfg.Name)
|
||||
}
|
||||
|
||||
func (o *organization) JwtToken() string {
|
||||
return o.cfgInternal.JWTSecret
|
||||
}
|
||||
|
||||
func (o *organization) GetGithubRegistrationToken() (string, error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"CreateOrganizationRegistrationToken", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
tk, ghResp, err := o.ghcli.CreateOrganizationRegistrationToken(o.ctx, o.cfg.Name)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"CreateOrganizationRegistrationToken", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return "", errors.Wrap(runnerErrors.ErrUnauthorized, "fetching token")
|
||||
}
|
||||
|
||||
return "", errors.Wrap(err, "creating runner token")
|
||||
}
|
||||
return *tk.Token, nil
|
||||
}
|
||||
|
||||
func (o *organization) String() string {
|
||||
return o.cfg.Name
|
||||
}
|
||||
|
||||
func (o *organization) WebhookSecret() string {
|
||||
return o.cfg.WebhookSecret
|
||||
}
|
||||
|
||||
func (o *organization) GetPoolByID(poolID string) (params.Pool, error) {
|
||||
pool, err := o.store.GetOrganizationPool(o.ctx, o.id, poolID)
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
func (o *organization) ValidateOwner(job params.WorkflowJob) error {
|
||||
if !strings.EqualFold(job.Organization.Login, o.cfg.Name) {
|
||||
return runnerErrors.NewBadRequestError("job not meant for this pool manager")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *organization) ID() string {
|
||||
return o.id
|
||||
}
|
||||
|
||||
func (o *organization) listHooks(ctx context.Context) ([]*github.Hook, error) {
|
||||
opts := github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
var allHooks []*github.Hook
|
||||
for {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListOrgHooks", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
hooks, ghResp, err := o.ghcli.ListOrgHooks(ctx, o.cfg.Name, &opts)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListOrgHooks", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusNotFound {
|
||||
return nil, runnerErrors.NewBadRequestError("organization not found or your PAT does not have access to manage webhooks")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching hooks")
|
||||
}
|
||||
allHooks = append(allHooks, hooks...)
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
return allHooks, nil
|
||||
}
|
||||
|
||||
func (o *organization) InstallHook(ctx context.Context, req *github.Hook) (params.HookInfo, error) {
|
||||
allHooks, err := o.listHooks(ctx)
|
||||
if err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
if err := validateHookRequest(o.cfgInternal.ControllerID, o.cfgInternal.BaseWebhookURL, allHooks, req); err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "validating hook request")
|
||||
}
|
||||
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"CreateOrgHook", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
|
||||
hook, _, err := o.ghcli.CreateOrgHook(ctx, o.cfg.Name, req)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"CreateOrgHook", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
return params.HookInfo{}, errors.Wrap(err, "creating organization hook")
|
||||
}
|
||||
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"PingOrgHook", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
|
||||
if _, err := o.ghcli.PingOrgHook(ctx, o.cfg.Name, hook.GetID()); err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"PingOrgHook", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to ping hook", "hook_id", hook.GetID())
|
||||
}
|
||||
|
||||
return hookToParamsHookInfo(hook), nil
|
||||
}
|
||||
|
||||
func (o *organization) UninstallHook(ctx context.Context, url string) error {
|
||||
allHooks, err := o.listHooks(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
for _, hook := range allHooks {
|
||||
if hook.Config["url"] == url {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"DeleteOrgHook", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
_, err = o.ghcli.DeleteOrgHook(ctx, o.cfg.Name, hook.GetID())
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"DeleteOrgHook", // label: operation
|
||||
metricsLabelOrganizationScope, // label: scope
|
||||
).Inc()
|
||||
return errors.Wrap(err, "deleting hook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *organization) GetHookInfo(ctx context.Context) (params.HookInfo, error) {
|
||||
allHooks, err := o.listHooks(ctx)
|
||||
if err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
for _, hook := range allHooks {
|
||||
hookInfo := hookToParamsHookInfo(hook)
|
||||
if strings.EqualFold(hookInfo.URL, o.cfgInternal.ControllerWebhookURL) {
|
||||
return hookInfo, nil
|
||||
}
|
||||
}
|
||||
|
||||
return params.HookInfo{}, runnerErrors.NewNotFoundError("hook not found")
|
||||
}
|
||||
|
|
@ -35,8 +35,10 @@ import (
|
|||
"github.com/cloudbase/garm-provider-common/util"
|
||||
"github.com/cloudbase/garm/auth"
|
||||
dbCommon "github.com/cloudbase/garm/database/common"
|
||||
"github.com/cloudbase/garm/metrics"
|
||||
"github.com/cloudbase/garm/params"
|
||||
"github.com/cloudbase/garm/runner/common"
|
||||
garmUtil "github.com/cloudbase/garm/util"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -58,10 +60,6 @@ const (
|
|||
// nolint:golangci-lint,godox
|
||||
// TODO: make this configurable(?)
|
||||
maxCreateAttempts = 5
|
||||
|
||||
metricsLabelEnterpriseScope = "Enterprise"
|
||||
metricsLabelRepositoryScope = "Repository"
|
||||
metricsLabelOrganizationScope = "Organization"
|
||||
)
|
||||
|
||||
type keyMutex struct {
|
||||
|
|
@ -96,9 +94,50 @@ type urls struct {
|
|||
webhookURL string
|
||||
controllerWebhookURL string
|
||||
}
|
||||
|
||||
func NewEntityPoolManager(ctx context.Context, entity params.GithubEntity, cfgInternal params.Internal, providers map[string]common.Provider, store dbCommon.Store) (common.PoolManager, error) {
|
||||
ctx = garmUtil.WithContext(ctx, slog.Any("pool_mgr", entity), slog.Any("pool_type", params.GithubEntityTypeRepository))
|
||||
ghc, err := garmUtil.GithubClient(ctx, entity, cfgInternal.GithubCredentialsDetails)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getting github client")
|
||||
}
|
||||
|
||||
if entity.WebhookSecret == "" {
|
||||
return nil, errors.New("webhook secret is empty")
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
keyMuxes := &keyMutex{}
|
||||
|
||||
repo := &basePoolManager{
|
||||
ctx: ctx,
|
||||
cfgInternal: cfgInternal,
|
||||
entity: entity,
|
||||
ghcli: ghc,
|
||||
|
||||
store: store,
|
||||
providers: providers,
|
||||
controllerID: cfgInternal.ControllerID,
|
||||
urls: urls{
|
||||
webhookURL: cfgInternal.BaseWebhookURL,
|
||||
callbackURL: cfgInternal.InstanceCallbackURL,
|
||||
metadataURL: cfgInternal.InstanceMetadataURL,
|
||||
controllerWebhookURL: cfgInternal.ControllerWebhookURL,
|
||||
},
|
||||
quit: make(chan struct{}),
|
||||
credsDetails: cfgInternal.GithubCredentialsDetails,
|
||||
wg: wg,
|
||||
keyMux: keyMuxes,
|
||||
}
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
type basePoolManager struct {
|
||||
ctx context.Context
|
||||
controllerID string
|
||||
entity params.GithubEntity
|
||||
ghcli common.GithubClient
|
||||
cfgInternal params.Internal
|
||||
|
||||
store dbCommon.Store
|
||||
|
||||
|
|
@ -106,7 +145,6 @@ type basePoolManager struct {
|
|||
tools []commonParams.RunnerApplicationDownload
|
||||
quit chan struct{}
|
||||
|
||||
helper poolHelper
|
||||
credsDetails params.GithubCredentials
|
||||
|
||||
managerIsRunning bool
|
||||
|
|
@ -120,7 +158,7 @@ type basePoolManager struct {
|
|||
}
|
||||
|
||||
func (r *basePoolManager) HandleWorkflowJob(job params.WorkflowJob) error {
|
||||
if err := r.helper.ValidateOwner(job); err != nil {
|
||||
if err := r.ValidateOwner(job); err != nil {
|
||||
return errors.Wrap(err, "validating owner")
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +183,7 @@ func (r *basePoolManager) HandleWorkflowJob(job params.WorkflowJob) error {
|
|||
return
|
||||
}
|
||||
// This job is new to us. Check if we have a pool that can handle it.
|
||||
potentialPools, err := r.store.FindPoolsMatchingAllTags(r.ctx, r.helper.PoolType(), r.helper.ID(), jobParams.Labels)
|
||||
potentialPools, err := r.store.FindPoolsMatchingAllTags(r.ctx, r.entity.EntityType, r.entity.ID, jobParams.Labels)
|
||||
if err != nil {
|
||||
slog.With(slog.Any("error", err)).ErrorContext(
|
||||
r.ctx, "failed to find pools matching tags; not recording job",
|
||||
|
|
@ -250,7 +288,7 @@ func (r *basePoolManager) HandleWorkflowJob(job params.WorkflowJob) error {
|
|||
|
||||
// A runner has picked up the job, and is now running it. It may need to be replaced if the pool has
|
||||
// a minimum number of idle runners configured.
|
||||
pool, err := r.store.GetPoolByID(r.ctx, instance.PoolID)
|
||||
pool, err := r.GetPoolByID(instance.PoolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting pool")
|
||||
}
|
||||
|
|
@ -332,12 +370,12 @@ func (r *basePoolManager) startLoopForFunction(f func() error, interval time.Dur
|
|||
|
||||
func (r *basePoolManager) updateTools() error {
|
||||
// Update tools cache.
|
||||
tools, err := r.helper.FetchTools()
|
||||
tools, err := r.FetchTools()
|
||||
if err != nil {
|
||||
slog.With(slog.Any("error", err)).ErrorContext(
|
||||
r.ctx, "failed to update tools for repo")
|
||||
r.setPoolRunningState(false, err.Error())
|
||||
return fmt.Errorf("failed to update tools for repo %s: %w", r.helper.String(), err)
|
||||
return fmt.Errorf("failed to update tools for repo %s: %w", r.entity.String(), err)
|
||||
}
|
||||
r.mux.Lock()
|
||||
r.tools = tools
|
||||
|
|
@ -387,7 +425,7 @@ func (r *basePoolManager) isManagedRunner(labels []string) bool {
|
|||
// If we were offline and did not process the webhook, the instance will linger.
|
||||
// We need to remove it from the provider and database.
|
||||
func (r *basePoolManager) cleanupOrphanedProviderRunners(runners []*github.Runner) error {
|
||||
dbInstances, err := r.helper.FetchDbInstances()
|
||||
dbInstances, err := r.FetchDbInstances()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching instances from db")
|
||||
}
|
||||
|
|
@ -422,7 +460,7 @@ func (r *basePoolManager) cleanupOrphanedProviderRunners(runners []*github.Runne
|
|||
continue
|
||||
}
|
||||
|
||||
pool, err := r.store.GetPoolByID(r.ctx, instance.PoolID)
|
||||
pool, err := r.GetPoolByID(instance.PoolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching instance pool info")
|
||||
}
|
||||
|
|
@ -463,7 +501,7 @@ func (r *basePoolManager) cleanupOrphanedProviderRunners(runners []*github.Runne
|
|||
// of "running" in the provider, but that has not registered with Github, and has
|
||||
// received no new updates in the configured timeout interval.
|
||||
func (r *basePoolManager) reapTimedOutRunners(runners []*github.Runner) error {
|
||||
dbInstances, err := r.helper.FetchDbInstances()
|
||||
dbInstances, err := r.FetchDbInstances()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching instances from db")
|
||||
}
|
||||
|
|
@ -492,7 +530,7 @@ func (r *basePoolManager) reapTimedOutRunners(runners []*github.Runner) error {
|
|||
}
|
||||
defer r.keyMux.Unlock(instance.Name, false)
|
||||
|
||||
pool, err := r.store.GetPoolByID(r.ctx, instance.PoolID)
|
||||
pool, err := r.GetPoolByID(instance.PoolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching instance pool info")
|
||||
}
|
||||
|
|
@ -560,7 +598,7 @@ func (r *basePoolManager) cleanupOrphanedGithubRunners(runners []*github.Runner)
|
|||
slog.InfoContext(
|
||||
r.ctx, "Runner has no database entry in garm, removing from github",
|
||||
"runner_name", runner.GetName())
|
||||
resp, err := r.helper.RemoveGithubRunner(*runner.ID)
|
||||
resp, err := r.RemoveGithubRunner(*runner.ID)
|
||||
if err != nil {
|
||||
// Removed in the meantime?
|
||||
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||
|
|
@ -594,7 +632,7 @@ func (r *basePoolManager) cleanupOrphanedGithubRunners(runners []*github.Runner)
|
|||
}
|
||||
}
|
||||
|
||||
pool, err := r.helper.GetPoolByID(dbInstance.PoolID)
|
||||
pool, err := r.GetPoolByID(dbInstance.PoolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -640,7 +678,7 @@ func (r *basePoolManager) cleanupOrphanedGithubRunners(runners []*github.Runner)
|
|||
slog.InfoContext(
|
||||
r.ctx, "Runner instance is no longer on the provider, removing from github",
|
||||
"runner_name", dbInstance.Name)
|
||||
resp, err := r.helper.RemoveGithubRunner(*runner.ID)
|
||||
resp, err := r.RemoveGithubRunner(*runner.ID)
|
||||
if err != nil {
|
||||
// Removed in the meantime?
|
||||
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||
|
|
@ -713,7 +751,7 @@ func (r *basePoolManager) fetchInstance(runnerName string) (params.Instance, err
|
|||
return params.Instance{}, errors.Wrap(err, "fetching instance")
|
||||
}
|
||||
|
||||
_, err = r.helper.GetPoolByID(runner.PoolID)
|
||||
_, err = r.GetPoolByID(runner.PoolID)
|
||||
if err != nil {
|
||||
return params.Instance{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -760,7 +798,7 @@ func (r *basePoolManager) setInstanceStatus(runnerName string, status commonPara
|
|||
}
|
||||
|
||||
func (r *basePoolManager) AddRunner(ctx context.Context, poolID string, aditionalLabels []string) (err error) {
|
||||
pool, err := r.helper.GetPoolByID(poolID)
|
||||
pool, err := r.GetPoolByID(poolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -778,7 +816,7 @@ func (r *basePoolManager) AddRunner(ctx context.Context, poolID string, aditiona
|
|||
|
||||
if !provider.DisableJITConfig() {
|
||||
// Attempt to create JIT config
|
||||
jitConfig, runner, err = r.helper.GetJITConfig(ctx, name, pool, labels)
|
||||
jitConfig, runner, err = r.ghcli.GetEntityJITConfig(ctx, name, pool, labels)
|
||||
if err != nil {
|
||||
slog.With(slog.Any("error", err)).ErrorContext(
|
||||
ctx, "failed to get JIT config, falling back to registration token")
|
||||
|
|
@ -819,7 +857,7 @@ func (r *basePoolManager) AddRunner(ctx context.Context, poolID string, aditiona
|
|||
}
|
||||
|
||||
if runner != nil {
|
||||
_, runnerCleanupErr := r.helper.RemoveGithubRunner(runner.GetID())
|
||||
_, runnerCleanupErr := r.RemoveGithubRunner(runner.GetID())
|
||||
if err != nil {
|
||||
slog.With(slog.Any("error", runnerCleanupErr)).ErrorContext(
|
||||
ctx, "failed to remove runner",
|
||||
|
|
@ -869,7 +907,7 @@ func (r *basePoolManager) getLabelsForInstance(pool params.Pool) []string {
|
|||
}
|
||||
|
||||
func (r *basePoolManager) addInstanceToProvider(instance params.Instance) error {
|
||||
pool, err := r.helper.GetPoolByID(instance.PoolID)
|
||||
pool, err := r.GetPoolByID(instance.PoolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -881,8 +919,8 @@ func (r *basePoolManager) addInstanceToProvider(instance params.Instance) error
|
|||
|
||||
jwtValidity := pool.RunnerTimeout()
|
||||
|
||||
entity := r.helper.String()
|
||||
jwtToken, err := auth.NewInstanceJWTToken(instance, r.helper.JwtToken(), entity, pool.PoolType(), jwtValidity)
|
||||
entity := r.entity.String()
|
||||
jwtToken, err := auth.NewInstanceJWTToken(instance, r.cfgInternal.JWTSecret, entity, pool.PoolType(), jwtValidity)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching instance jwt token")
|
||||
}
|
||||
|
|
@ -892,7 +930,7 @@ func (r *basePoolManager) addInstanceToProvider(instance params.Instance) error
|
|||
bootstrapArgs := commonParams.BootstrapInstance{
|
||||
Name: instance.Name,
|
||||
Tools: r.tools,
|
||||
RepoURL: r.helper.GithubURL(),
|
||||
RepoURL: r.GithubURL(),
|
||||
MetadataURL: instance.MetadataURL,
|
||||
CallbackURL: instance.CallbackURL,
|
||||
InstanceToken: jwtToken,
|
||||
|
|
@ -965,7 +1003,7 @@ func (r *basePoolManager) getRunnerDetailsFromJob(job params.WorkflowJob) (param
|
|||
slog.InfoContext(
|
||||
r.ctx, "runner name not found in workflow job, attempting to fetch from API",
|
||||
"job_id", job.WorkflowJob.ID)
|
||||
runnerInfo, err = r.helper.GetRunnerInfoFromWorkflow(job)
|
||||
runnerInfo, err = r.GetRunnerInfoFromWorkflow(job)
|
||||
if err != nil {
|
||||
return params.RunnerInfo{}, errors.Wrap(err, "fetching runner name from API")
|
||||
}
|
||||
|
|
@ -1035,15 +1073,15 @@ func (r *basePoolManager) paramsWorkflowJobToParamsJob(job params.WorkflowJob) (
|
|||
|
||||
jobParams.RunnerName = runnerName
|
||||
|
||||
switch r.helper.PoolType() {
|
||||
case params.EnterprisePool:
|
||||
switch r.entity.EntityType {
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
jobParams.EnterpriseID = &asUUID
|
||||
case params.RepositoryPool:
|
||||
case params.GithubEntityTypeRepository:
|
||||
jobParams.RepoID = &asUUID
|
||||
case params.OrganizationPool:
|
||||
case params.GithubEntityTypeOrganization:
|
||||
jobParams.OrgID = &asUUID
|
||||
default:
|
||||
return jobParams, errors.Errorf("unknown pool type: %s", r.helper.PoolType())
|
||||
return jobParams, errors.Errorf("unknown pool type: %s", r.entity.EntityType)
|
||||
}
|
||||
|
||||
return jobParams, nil
|
||||
|
|
@ -1147,7 +1185,7 @@ func (r *basePoolManager) scaleDownOnePool(ctx context.Context, pool params.Pool
|
|||
// nolint:golangci-lint,godox
|
||||
// TODO: should probably allow aditional filters to list functions. Would help to filter by date
|
||||
// instead of returning a bunch of results and filtering manually.
|
||||
queued, err := r.store.ListEntityJobsByStatus(r.ctx, r.helper.PoolType(), r.helper.ID(), params.JobStatusQueued)
|
||||
queued, err := r.store.ListEntityJobsByStatus(r.ctx, r.entity.EntityType, r.entity.ID, params.JobStatusQueued)
|
||||
if err != nil && !errors.Is(err, runnerErrors.ErrNotFound) {
|
||||
return errors.Wrap(err, "listing queued jobs")
|
||||
}
|
||||
|
|
@ -1328,7 +1366,7 @@ func (r *basePoolManager) retryFailedInstancesForOnePool(ctx context.Context, po
|
|||
}
|
||||
|
||||
func (r *basePoolManager) retryFailedInstances() error {
|
||||
pools, err := r.helper.ListPools()
|
||||
pools, err := r.ListPools()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing pools: %w", err)
|
||||
}
|
||||
|
|
@ -1351,7 +1389,7 @@ func (r *basePoolManager) retryFailedInstances() error {
|
|||
}
|
||||
|
||||
func (r *basePoolManager) scaleDown() error {
|
||||
pools, err := r.helper.ListPools()
|
||||
pools, err := r.ListPools()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing pools: %w", err)
|
||||
}
|
||||
|
|
@ -1372,7 +1410,7 @@ func (r *basePoolManager) scaleDown() error {
|
|||
}
|
||||
|
||||
func (r *basePoolManager) ensureMinIdleRunners() error {
|
||||
pools, err := r.helper.ListPools()
|
||||
pools, err := r.ListPools()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing pools: %w", err)
|
||||
}
|
||||
|
|
@ -1392,7 +1430,7 @@ func (r *basePoolManager) ensureMinIdleRunners() error {
|
|||
}
|
||||
|
||||
func (r *basePoolManager) deleteInstanceFromProvider(ctx context.Context, instance params.Instance) error {
|
||||
pool, err := r.helper.GetPoolByID(instance.PoolID)
|
||||
pool, err := r.GetPoolByID(instance.PoolID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
|
|
@ -1422,7 +1460,7 @@ func (r *basePoolManager) deleteInstanceFromProvider(ctx context.Context, instan
|
|||
}
|
||||
|
||||
func (r *basePoolManager) deletePendingInstances() error {
|
||||
instances, err := r.helper.FetchDbInstances()
|
||||
instances, err := r.FetchDbInstances()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch instances from store: %w", err)
|
||||
}
|
||||
|
|
@ -1510,7 +1548,7 @@ func (r *basePoolManager) deletePendingInstances() error {
|
|||
func (r *basePoolManager) addPendingInstances() error {
|
||||
// nolint:golangci-lint,godox
|
||||
// TODO: filter instances by status.
|
||||
instances, err := r.helper.FetchDbInstances()
|
||||
instances, err := r.FetchDbInstances()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch instances from store: %w", err)
|
||||
}
|
||||
|
|
@ -1587,7 +1625,7 @@ func (r *basePoolManager) Wait() error {
|
|||
func (r *basePoolManager) runnerCleanup() (err error) {
|
||||
slog.DebugContext(
|
||||
r.ctx, "running runner cleanup")
|
||||
runners, err := r.helper.GetGithubRunners()
|
||||
runners, err := r.GetGithubRunners()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch github runners: %w", err)
|
||||
}
|
||||
|
|
@ -1653,9 +1691,28 @@ func (r *basePoolManager) Stop() error {
|
|||
}
|
||||
|
||||
func (r *basePoolManager) RefreshState(param params.UpdatePoolStateParams) error {
|
||||
if err := r.helper.UpdateState(param); err != nil {
|
||||
return fmt.Errorf("failed to update pool state: %w", err)
|
||||
r.mux.Lock()
|
||||
|
||||
if param.WebhookSecret != "" {
|
||||
r.entity.WebhookSecret = param.WebhookSecret
|
||||
}
|
||||
if param.InternalConfig != nil {
|
||||
r.cfgInternal = *param.InternalConfig
|
||||
r.urls = urls{
|
||||
webhookURL: r.cfgInternal.BaseWebhookURL,
|
||||
callbackURL: r.cfgInternal.InstanceCallbackURL,
|
||||
metadataURL: r.cfgInternal.InstanceMetadataURL,
|
||||
controllerWebhookURL: r.cfgInternal.ControllerWebhookURL,
|
||||
}
|
||||
}
|
||||
|
||||
ghc, err := garmUtil.GithubClient(r.ctx, r.entity, r.cfgInternal.GithubCredentialsDetails)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting github client")
|
||||
}
|
||||
r.ghcli = ghc
|
||||
r.mux.Unlock()
|
||||
|
||||
// Update the tools as soon as state is updated. This should revive a stopped pool manager
|
||||
// or stop one if the supplied credentials are not okay.
|
||||
if err := r.updateTools(); err != nil {
|
||||
|
|
@ -1665,25 +1722,25 @@ func (r *basePoolManager) RefreshState(param params.UpdatePoolStateParams) error
|
|||
}
|
||||
|
||||
func (r *basePoolManager) WebhookSecret() string {
|
||||
return r.helper.WebhookSecret()
|
||||
return r.entity.WebhookSecret
|
||||
}
|
||||
|
||||
func (r *basePoolManager) GithubRunnerRegistrationToken() (string, error) {
|
||||
return r.helper.GetGithubRegistrationToken()
|
||||
return r.GetGithubRegistrationToken()
|
||||
}
|
||||
|
||||
func (r *basePoolManager) ID() string {
|
||||
return r.helper.ID()
|
||||
return r.entity.ID
|
||||
}
|
||||
|
||||
// Delete runner will delete a runner from a pool. If forceRemove is set to true, any error received from
|
||||
// the IaaS provider will be ignored and deletion will continue.
|
||||
func (r *basePoolManager) DeleteRunner(runner params.Instance, forceRemove, bypassGHUnauthorizedError bool) error {
|
||||
if !r.managerIsRunning && !bypassGHUnauthorizedError {
|
||||
return runnerErrors.NewConflictError("pool manager is not running for %s", r.helper.String())
|
||||
return runnerErrors.NewConflictError("pool manager is not running for %s", r.entity.String())
|
||||
}
|
||||
if runner.AgentID != 0 {
|
||||
resp, err := r.helper.RemoveGithubRunner(runner.AgentID)
|
||||
resp, err := r.RemoveGithubRunner(runner.AgentID)
|
||||
if err != nil {
|
||||
if resp != nil {
|
||||
switch resp.StatusCode {
|
||||
|
|
@ -1767,13 +1824,13 @@ func (r *basePoolManager) DeleteRunner(runner params.Instance, forceRemove, bypa
|
|||
// so those will trigger the creation of a runner. The jobs we don't know about will be dealt with by the idle runners.
|
||||
// Once jobs are consumed, you can set min-idle-runners to 0 again.
|
||||
func (r *basePoolManager) consumeQueuedJobs() error {
|
||||
queued, err := r.store.ListEntityJobsByStatus(r.ctx, r.helper.PoolType(), r.helper.ID(), params.JobStatusQueued)
|
||||
queued, err := r.store.ListEntityJobsByStatus(r.ctx, r.entity.EntityType, r.entity.ID, params.JobStatusQueued)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "listing queued jobs")
|
||||
}
|
||||
|
||||
poolsCache := poolsForTags{
|
||||
poolCacheType: r.helper.PoolBalancerType(),
|
||||
poolCacheType: r.PoolBalancerType(),
|
||||
}
|
||||
|
||||
slog.DebugContext(
|
||||
|
|
@ -1825,7 +1882,7 @@ func (r *basePoolManager) consumeQueuedJobs() error {
|
|||
|
||||
poolRR, ok := poolsCache.Get(job.Labels)
|
||||
if !ok {
|
||||
potentialPools, err := r.store.FindPoolsMatchingAllTags(r.ctx, r.helper.PoolType(), r.helper.ID(), job.Labels)
|
||||
potentialPools, err := r.store.FindPoolsMatchingAllTags(r.ctx, r.entity.EntityType, r.entity.ID, job.Labels)
|
||||
if err != nil {
|
||||
slog.With(slog.Any("error", err)).ErrorContext(
|
||||
r.ctx, "error finding pools matching labels")
|
||||
|
|
@ -1896,6 +1953,73 @@ func (r *basePoolManager) consumeQueuedJobs() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *basePoolManager) UninstallWebhook(ctx context.Context) error {
|
||||
if r.urls.controllerWebhookURL == "" {
|
||||
return errors.Wrap(runnerErrors.ErrBadRequest, "controller webhook url is empty")
|
||||
}
|
||||
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
var controllerHookID int64
|
||||
var baseHook string
|
||||
trimmedBase := strings.TrimRight(r.urls.webhookURL, "/")
|
||||
trimmedController := strings.TrimRight(r.urls.controllerWebhookURL, "/")
|
||||
|
||||
for _, hook := range allHooks {
|
||||
hookInfo := hookToParamsHookInfo(hook)
|
||||
info := strings.TrimRight(hookInfo.URL, "/")
|
||||
if strings.EqualFold(info, trimmedController) {
|
||||
controllerHookID = hook.GetID()
|
||||
}
|
||||
|
||||
if strings.EqualFold(info, trimmedBase) {
|
||||
baseHook = hookInfo.URL
|
||||
}
|
||||
}
|
||||
|
||||
if controllerHookID != 0 {
|
||||
_, err = r.ghcli.DeleteEntityHook(ctx, controllerHookID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting hook: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if baseHook != "" {
|
||||
return runnerErrors.NewBadRequestError("base hook found (%s) and must be deleted manually", baseHook)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *basePoolManager) InstallHook(ctx context.Context, req *github.Hook) (params.HookInfo, error) {
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
if err := validateHookRequest(r.cfgInternal.ControllerID, r.cfgInternal.BaseWebhookURL, allHooks, req); err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "validating hook request")
|
||||
}
|
||||
|
||||
hook, err := r.ghcli.CreateEntityHook(ctx, req)
|
||||
if err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "creating entity hook")
|
||||
}
|
||||
|
||||
if _, err := r.ghcli.PingEntityHook(ctx, hook.GetID()); err != nil {
|
||||
slog.With(slog.Any("error", err)).ErrorContext(
|
||||
ctx, "failed to ping hook",
|
||||
"hook_id", hook.GetID(),
|
||||
"entity", r.entity)
|
||||
}
|
||||
|
||||
return hookToParamsHookInfo(hook), nil
|
||||
}
|
||||
|
||||
func (r *basePoolManager) InstallWebhook(ctx context.Context, param params.InstallWebhookParams) (params.HookInfo, error) {
|
||||
if r.urls.controllerWebhookURL == "" {
|
||||
return params.HookInfo{}, errors.Wrap(runnerErrors.ErrBadRequest, "controller webhook url is empty")
|
||||
|
|
@ -1918,19 +2042,212 @@ func (r *basePoolManager) InstallWebhook(ctx context.Context, param params.Insta
|
|||
},
|
||||
}
|
||||
|
||||
return r.helper.InstallHook(ctx, req)
|
||||
return r.InstallHook(ctx, req)
|
||||
}
|
||||
|
||||
func (r *basePoolManager) UninstallWebhook(ctx context.Context) error {
|
||||
if r.urls.controllerWebhookURL == "" {
|
||||
return errors.Wrap(runnerErrors.ErrBadRequest, "controller webhook url is empty")
|
||||
func (r *basePoolManager) ValidateOwner(job params.WorkflowJob) error {
|
||||
switch r.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
if !strings.EqualFold(job.Repository.Name, r.entity.Name) || !strings.EqualFold(job.Repository.Owner.Login, r.entity.Owner) {
|
||||
return runnerErrors.NewBadRequestError("job not meant for this pool manager")
|
||||
}
|
||||
case params.GithubEntityTypeOrganization:
|
||||
if !strings.EqualFold(job.Organization.Login, r.entity.Owner) {
|
||||
return runnerErrors.NewBadRequestError("job not meant for this pool manager")
|
||||
}
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
if !strings.EqualFold(job.Enterprise.Slug, r.entity.Owner) {
|
||||
return runnerErrors.NewBadRequestError("job not meant for this pool manager")
|
||||
}
|
||||
default:
|
||||
return runnerErrors.NewBadRequestError("unknown entity type")
|
||||
}
|
||||
|
||||
return r.helper.UninstallHook(ctx, r.urls.controllerWebhookURL)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *basePoolManager) GetRunnerInfoFromWorkflow(job params.WorkflowJob) (params.RunnerInfo, error) {
|
||||
if err := r.ValidateOwner(job); err != nil {
|
||||
return params.RunnerInfo{}, errors.Wrap(err, "validating owner")
|
||||
}
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"GetWorkflowJobByID", // label: operation
|
||||
r.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
workflow, ghResp, err := r.ghcli.GetWorkflowJobByID(r.ctx, job.Repository.Owner.Login, job.Repository.Name, job.WorkflowJob.ID)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"GetWorkflowJobByID", // label: operation
|
||||
r.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return params.RunnerInfo{}, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching workflow info")
|
||||
}
|
||||
return params.RunnerInfo{}, errors.Wrap(err, "fetching workflow info")
|
||||
}
|
||||
|
||||
if workflow.RunnerName != nil {
|
||||
return params.RunnerInfo{
|
||||
Name: *workflow.RunnerName,
|
||||
Labels: workflow.Labels,
|
||||
}, nil
|
||||
}
|
||||
return params.RunnerInfo{}, fmt.Errorf("failed to find runner name from workflow")
|
||||
}
|
||||
|
||||
func (r *basePoolManager) GetGithubRegistrationToken() (string, error) {
|
||||
tk, ghResp, err := r.ghcli.CreateEntityRegistrationToken(r.ctx)
|
||||
if err != nil {
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return "", errors.Wrap(runnerErrors.ErrUnauthorized, "fetching token")
|
||||
}
|
||||
return "", errors.Wrap(err, "creating runner token")
|
||||
}
|
||||
return *tk.Token, nil
|
||||
}
|
||||
|
||||
func (r *basePoolManager) RemoveGithubRunner(runnerID int64) (*github.Response, error) {
|
||||
ghResp, err := r.ghcli.RemoveEntityRunner(r.ctx, runnerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("removing runner: %w", err)
|
||||
}
|
||||
return ghResp, nil
|
||||
}
|
||||
|
||||
func (r *basePoolManager) FetchTools() ([]commonParams.RunnerApplicationDownload, error) {
|
||||
tools, ghResp, err := r.ghcli.ListEntityRunnerApplicationDownloads(r.ctx)
|
||||
if err != nil {
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching tools")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runner tools")
|
||||
}
|
||||
|
||||
ret := []commonParams.RunnerApplicationDownload{}
|
||||
for _, tool := range tools {
|
||||
if tool == nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, commonParams.RunnerApplicationDownload(*tool))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *basePoolManager) GetGithubRunners() ([]*github.Runner, error) {
|
||||
opts := github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
var allRunners []*github.Runner
|
||||
|
||||
for {
|
||||
runners, ghResp, err := r.ghcli.ListEntityRunners(r.ctx, &opts)
|
||||
if err != nil {
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runners")
|
||||
}
|
||||
allRunners = append(allRunners, runners.Runners...)
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
|
||||
return allRunners, nil
|
||||
}
|
||||
|
||||
func (r *basePoolManager) PoolBalancerType() params.PoolBalancerType {
|
||||
if r.cfgInternal.PoolBalancerType == "" {
|
||||
return params.PoolBalancerTypeRoundRobin
|
||||
}
|
||||
return r.cfgInternal.PoolBalancerType
|
||||
}
|
||||
|
||||
func (r *basePoolManager) GithubURL() string {
|
||||
switch r.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
return fmt.Sprintf("%s/%s/%s", r.cfgInternal.GithubCredentialsDetails.BaseURL, r.entity.Owner, r.entity.Name)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
return fmt.Sprintf("%s/%s", r.cfgInternal.GithubCredentialsDetails.BaseURL, r.entity.Owner)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
return fmt.Sprintf("%s/enterprises/%s", r.cfgInternal.GithubCredentialsDetails.BaseURL, r.entity.Owner)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *basePoolManager) FetchDbInstances() ([]params.Instance, error) {
|
||||
switch r.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
return r.store.ListRepoInstances(r.ctx, r.entity.ID)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
return r.store.ListOrgInstances(r.ctx, r.entity.ID)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
return r.store.ListEnterpriseInstances(r.ctx, r.entity.ID)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown entity type: %s", r.entity.EntityType)
|
||||
}
|
||||
|
||||
func (r *basePoolManager) ListPools() ([]params.Pool, error) {
|
||||
switch r.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
return r.store.ListRepoPools(r.ctx, r.entity.ID)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
return r.store.ListOrgPools(r.ctx, r.entity.ID)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
return r.store.ListEnterprisePools(r.ctx, r.entity.ID)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown entity type: %s", r.entity.EntityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *basePoolManager) GetPoolByID(poolID string) (params.Pool, error) {
|
||||
switch r.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
return r.store.GetRepositoryPool(r.ctx, r.entity.ID, poolID)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
return r.store.GetOrganizationPool(r.ctx, r.entity.ID, poolID)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
return r.store.GetEnterprisePool(r.ctx, r.entity.ID, poolID)
|
||||
default:
|
||||
return params.Pool{}, fmt.Errorf("unknown entity type: %s", r.entity.EntityType)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *basePoolManager) GetWebhookInfo(ctx context.Context) (params.HookInfo, error) {
|
||||
return r.helper.GetHookInfo(ctx)
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
trimmedBase := strings.TrimRight(r.urls.webhookURL, "/")
|
||||
trimmedController := strings.TrimRight(r.urls.controllerWebhookURL, "/")
|
||||
|
||||
var controllerHookInfo *params.HookInfo
|
||||
var baseHookInfo *params.HookInfo
|
||||
|
||||
for _, hook := range allHooks {
|
||||
hookInfo := hookToParamsHookInfo(hook)
|
||||
info := strings.TrimRight(hookInfo.URL, "/")
|
||||
if strings.EqualFold(info, trimmedController) {
|
||||
controllerHookInfo = &hookInfo
|
||||
break
|
||||
}
|
||||
if strings.EqualFold(info, trimmedBase) {
|
||||
baseHookInfo = &hookInfo
|
||||
}
|
||||
}
|
||||
|
||||
// Return the controller hook info if available.
|
||||
if controllerHookInfo != nil {
|
||||
return *controllerHookInfo, nil
|
||||
}
|
||||
|
||||
// Fall back to base hook info if defined.
|
||||
if baseHookInfo != nil {
|
||||
return *baseHookInfo, nil
|
||||
}
|
||||
|
||||
return params.HookInfo{}, runnerErrors.NewNotFoundError("hook not found")
|
||||
}
|
||||
|
||||
func (r *basePoolManager) RootCABundle() (params.CertificateBundle, error) {
|
||||
|
|
|
|||
|
|
@ -1,472 +0,0 @@
|
|||
// 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.
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
runnerErrors "github.com/cloudbase/garm-provider-common/errors"
|
||||
commonParams "github.com/cloudbase/garm-provider-common/params"
|
||||
dbCommon "github.com/cloudbase/garm/database/common"
|
||||
"github.com/cloudbase/garm/metrics"
|
||||
"github.com/cloudbase/garm/params"
|
||||
"github.com/cloudbase/garm/runner/common"
|
||||
"github.com/cloudbase/garm/util"
|
||||
)
|
||||
|
||||
// test that we implement PoolManager
|
||||
var _ poolHelper = &repository{}
|
||||
|
||||
func NewRepositoryPoolManager(ctx context.Context, cfg params.Repository, cfgInternal params.Internal, providers map[string]common.Provider, store dbCommon.Store) (common.PoolManager, error) {
|
||||
ctx = util.WithContext(ctx, slog.Any("pool_mgr", fmt.Sprintf("%s/%s", cfg.Owner, cfg.Name)), slog.Any("pool_type", params.RepositoryPool))
|
||||
ghc, _, err := util.GithubClient(ctx, cfgInternal.GithubCredentialsDetails)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getting github client")
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
keyMuxes := &keyMutex{}
|
||||
|
||||
helper := &repository{
|
||||
cfg: cfg,
|
||||
cfgInternal: cfgInternal,
|
||||
ctx: ctx,
|
||||
ghcli: ghc,
|
||||
id: cfg.ID,
|
||||
store: store,
|
||||
}
|
||||
|
||||
repo := &basePoolManager{
|
||||
ctx: ctx,
|
||||
store: store,
|
||||
providers: providers,
|
||||
controllerID: cfgInternal.ControllerID,
|
||||
urls: urls{
|
||||
webhookURL: cfgInternal.BaseWebhookURL,
|
||||
callbackURL: cfgInternal.InstanceCallbackURL,
|
||||
metadataURL: cfgInternal.InstanceMetadataURL,
|
||||
controllerWebhookURL: cfgInternal.ControllerWebhookURL,
|
||||
},
|
||||
quit: make(chan struct{}),
|
||||
helper: helper,
|
||||
credsDetails: cfgInternal.GithubCredentialsDetails,
|
||||
wg: wg,
|
||||
keyMux: keyMuxes,
|
||||
}
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
var _ poolHelper = &repository{}
|
||||
|
||||
type repository struct {
|
||||
cfg params.Repository
|
||||
cfgInternal params.Internal
|
||||
ctx context.Context
|
||||
ghcli common.GithubClient
|
||||
id string
|
||||
store dbCommon.Store
|
||||
|
||||
mux sync.Mutex
|
||||
}
|
||||
|
||||
func (r *repository) PoolBalancerType() params.PoolBalancerType {
|
||||
if r.cfgInternal.PoolBalancerType == "" {
|
||||
return params.PoolBalancerTypeRoundRobin
|
||||
}
|
||||
return r.cfgInternal.PoolBalancerType
|
||||
}
|
||||
|
||||
// nolint:golint,revive
|
||||
// pool is used in enterprise and organzation
|
||||
func (r *repository) GetJITConfig(ctx context.Context, instance string, pool params.Pool, labels []string) (jitConfigMap map[string]string, runner *github.Runner, err error) {
|
||||
req := github.GenerateJITConfigRequest{
|
||||
Name: instance,
|
||||
// At the repository level we only have the default runner group.
|
||||
RunnerGroupID: 1,
|
||||
Labels: labels,
|
||||
// nolint:golangci-lint,godox
|
||||
// TODO(gabriel-samfira): Should we make this configurable?
|
||||
WorkFolder: github.String("_work"),
|
||||
}
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"GenerateRepoJITConfig", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
jitConfig, resp, err := r.ghcli.GenerateRepoJITConfig(ctx, r.cfg.Owner, r.cfg.Name, &req)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"GenerateRepoJITConfig", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
if resp != nil && resp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, nil, fmt.Errorf("failed to get JIT config: %w", err)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to get JIT config: %w", err)
|
||||
}
|
||||
runner = jitConfig.Runner
|
||||
|
||||
defer func() {
|
||||
if err != nil && runner != nil {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
_, innerErr := r.ghcli.RemoveRunner(r.ctx, r.cfg.Owner, r.cfg.Name, runner.GetID())
|
||||
if innerErr != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
}
|
||||
slog.With(slog.Any("error", innerErr)).ErrorContext(
|
||||
ctx, "failed to remove runner",
|
||||
"runner_id", runner.GetID(),
|
||||
"repo", r.cfg.Name,
|
||||
"owner", r.cfg.Owner)
|
||||
}
|
||||
}()
|
||||
|
||||
decoded, err := base64.StdEncoding.DecodeString(jitConfig.GetEncodedJITConfig())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode JIT config: %w", err)
|
||||
}
|
||||
|
||||
var ret map[string]string
|
||||
if err := json.Unmarshal(decoded, &ret); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal JIT config: %w", err)
|
||||
}
|
||||
|
||||
return ret, runner, nil
|
||||
}
|
||||
|
||||
func (r *repository) PoolType() params.PoolType {
|
||||
return params.RepositoryPool
|
||||
}
|
||||
|
||||
func (r *repository) GetRunnerInfoFromWorkflow(job params.WorkflowJob) (params.RunnerInfo, error) {
|
||||
if err := r.ValidateOwner(job); err != nil {
|
||||
return params.RunnerInfo{}, errors.Wrap(err, "validating owner")
|
||||
}
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"GetWorkflowJobByID", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
workflow, ghResp, err := r.ghcli.GetWorkflowJobByID(r.ctx, job.Repository.Owner.Login, job.Repository.Name, job.WorkflowJob.ID)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"GetWorkflowJobByID", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return params.RunnerInfo{}, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching workflow info")
|
||||
}
|
||||
return params.RunnerInfo{}, errors.Wrap(err, "fetching workflow info")
|
||||
}
|
||||
|
||||
if workflow.RunnerName != nil {
|
||||
return params.RunnerInfo{
|
||||
Name: *workflow.RunnerName,
|
||||
Labels: workflow.Labels,
|
||||
}, nil
|
||||
}
|
||||
return params.RunnerInfo{}, fmt.Errorf("failed to find runner name from workflow")
|
||||
}
|
||||
|
||||
func (r *repository) UpdateState(param params.UpdatePoolStateParams) error {
|
||||
r.mux.Lock()
|
||||
defer r.mux.Unlock()
|
||||
|
||||
r.cfg.WebhookSecret = param.WebhookSecret
|
||||
if param.InternalConfig != nil {
|
||||
r.cfgInternal = *param.InternalConfig
|
||||
}
|
||||
|
||||
ghc, _, err := util.GithubClient(r.ctx, r.cfgInternal.GithubCredentialsDetails)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting github client")
|
||||
}
|
||||
r.ghcli = ghc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *repository) GetGithubRunners() ([]*github.Runner, error) {
|
||||
opts := github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
|
||||
var allRunners []*github.Runner
|
||||
for {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListRunners", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
runners, ghResp, err := r.ghcli.ListRunners(r.ctx, r.cfg.Owner, r.cfg.Name, &opts)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListRunners", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runners")
|
||||
}
|
||||
allRunners = append(allRunners, runners.Runners...)
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
|
||||
return allRunners, nil
|
||||
}
|
||||
|
||||
func (r *repository) FetchTools() ([]commonParams.RunnerApplicationDownload, error) {
|
||||
r.mux.Lock()
|
||||
defer r.mux.Unlock()
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListRunnerApplicationDownloads", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
tools, ghResp, err := r.ghcli.ListRunnerApplicationDownloads(r.ctx, r.cfg.Owner, r.cfg.Name)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListRunnerApplicationDownloads", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching tools")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching runner tools")
|
||||
}
|
||||
|
||||
ret := []commonParams.RunnerApplicationDownload{}
|
||||
for _, tool := range tools {
|
||||
if tool == nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, commonParams.RunnerApplicationDownload(*tool))
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *repository) FetchDbInstances() ([]params.Instance, error) {
|
||||
return r.store.ListRepoInstances(r.ctx, r.id)
|
||||
}
|
||||
|
||||
func (r *repository) RemoveGithubRunner(runnerID int64) (*github.Response, error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
ghResp, err := r.ghcli.RemoveRunner(r.ctx, r.cfg.Owner, r.cfg.Name, runnerID)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"RemoveRunner", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ghResp, nil
|
||||
}
|
||||
|
||||
func (r *repository) ListPools() ([]params.Pool, error) {
|
||||
pools, err := r.store.ListRepoPools(r.ctx, r.id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching pools")
|
||||
}
|
||||
return pools, nil
|
||||
}
|
||||
|
||||
func (r *repository) GithubURL() string {
|
||||
return fmt.Sprintf("%s/%s/%s", r.cfgInternal.GithubCredentialsDetails.BaseURL, r.cfg.Owner, r.cfg.Name)
|
||||
}
|
||||
|
||||
func (r *repository) JwtToken() string {
|
||||
return r.cfgInternal.JWTSecret
|
||||
}
|
||||
|
||||
func (r *repository) GetGithubRegistrationToken() (string, error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"CreateRegistrationToken", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
tk, ghResp, err := r.ghcli.CreateRegistrationToken(r.ctx, r.cfg.Owner, r.cfg.Name)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"CreateRegistrationToken", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return "", errors.Wrap(runnerErrors.ErrUnauthorized, "fetching token")
|
||||
}
|
||||
return "", errors.Wrap(err, "creating runner token")
|
||||
}
|
||||
return *tk.Token, nil
|
||||
}
|
||||
|
||||
func (r *repository) String() string {
|
||||
return fmt.Sprintf("%s/%s", r.cfg.Owner, r.cfg.Name)
|
||||
}
|
||||
|
||||
func (r *repository) WebhookSecret() string {
|
||||
return r.cfg.WebhookSecret
|
||||
}
|
||||
|
||||
func (r *repository) GetPoolByID(poolID string) (params.Pool, error) {
|
||||
pool, err := r.store.GetRepositoryPool(r.ctx, r.id, poolID)
|
||||
if err != nil {
|
||||
return params.Pool{}, errors.Wrap(err, "fetching pool")
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
func (r *repository) ValidateOwner(job params.WorkflowJob) error {
|
||||
if !strings.EqualFold(job.Repository.Name, r.cfg.Name) || !strings.EqualFold(job.Repository.Owner.Login, r.cfg.Owner) {
|
||||
return runnerErrors.NewBadRequestError("job not meant for this pool manager")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *repository) ID() string {
|
||||
return r.id
|
||||
}
|
||||
|
||||
func (r *repository) listHooks(ctx context.Context) ([]*github.Hook, error) {
|
||||
opts := github.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
var allHooks []*github.Hook
|
||||
for {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListRepoHooks", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
hooks, ghResp, err := r.ghcli.ListRepoHooks(ctx, r.cfg.Owner, r.cfg.Name, &opts)
|
||||
if err != nil {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListRepoHooks", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusNotFound {
|
||||
return nil, runnerErrors.NewBadRequestError("repository not found or your PAT does not have access to manage webhooks")
|
||||
}
|
||||
return nil, errors.Wrap(err, "fetching hooks")
|
||||
}
|
||||
allHooks = append(allHooks, hooks...)
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
return allHooks, nil
|
||||
}
|
||||
|
||||
func (r *repository) InstallHook(ctx context.Context, req *github.Hook) (params.HookInfo, error) {
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
if err := validateHookRequest(r.cfgInternal.ControllerID, r.cfgInternal.BaseWebhookURL, allHooks, req); err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "validating hook request")
|
||||
}
|
||||
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"CreateRepoHook", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
|
||||
hook, _, err := r.ghcli.CreateRepoHook(ctx, r.cfg.Owner, r.cfg.Name, req)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"CreateRepoHook", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
return params.HookInfo{}, errors.Wrap(err, "creating repository hook")
|
||||
}
|
||||
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"PingRepoHook", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
|
||||
if _, err := r.ghcli.PingRepoHook(ctx, r.cfg.Owner, r.cfg.Name, hook.GetID()); err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"PingRepoHook", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
slog.With(slog.Any("error", err)).ErrorContext(
|
||||
ctx, "failed to ping hook",
|
||||
"hook_id", hook.GetID(),
|
||||
"repo", r.cfg.Name,
|
||||
"owner", r.cfg.Owner)
|
||||
}
|
||||
|
||||
return hookToParamsHookInfo(hook), nil
|
||||
}
|
||||
|
||||
func (r *repository) UninstallHook(ctx context.Context, url string) error {
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
for _, hook := range allHooks {
|
||||
if hook.Config["url"] == url {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"DeleteRepoHook", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
_, err = r.ghcli.DeleteRepoHook(ctx, r.cfg.Owner, r.cfg.Name, hook.GetID())
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"DeleteRepoHook", // label: operation
|
||||
metricsLabelRepositoryScope, // label: scope
|
||||
).Inc()
|
||||
return errors.Wrap(err, "deleting hook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *repository) GetHookInfo(ctx context.Context) (params.HookInfo, error) {
|
||||
allHooks, err := r.listHooks(ctx)
|
||||
if err != nil {
|
||||
return params.HookInfo{}, errors.Wrap(err, "listing hooks")
|
||||
}
|
||||
|
||||
for _, hook := range allHooks {
|
||||
hookInfo := hookToParamsHookInfo(hook)
|
||||
if strings.EqualFold(hookInfo.URL, r.cfgInternal.ControllerWebhookURL) {
|
||||
return hookInfo, nil
|
||||
}
|
||||
}
|
||||
return params.HookInfo{}, runnerErrors.NewNotFoundError("hook not found")
|
||||
}
|
||||
|
|
@ -109,7 +109,14 @@ func (p *poolManagerCtrl) CreateRepoPoolManager(ctx context.Context, repo params
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching internal config")
|
||||
}
|
||||
poolManager, err := pool.NewRepositoryPoolManager(ctx, repo, cfgInternal, providers, store)
|
||||
entity := params.GithubEntity{
|
||||
Owner: repo.Owner,
|
||||
Name: repo.Name,
|
||||
ID: repo.ID,
|
||||
WebhookSecret: repo.WebhookSecret,
|
||||
EntityType: params.GithubEntityTypeRepository,
|
||||
}
|
||||
poolManager, err := pool.NewEntityPoolManager(ctx, entity, cfgInternal, providers, store)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creating repo pool manager")
|
||||
}
|
||||
|
|
@ -175,7 +182,13 @@ func (p *poolManagerCtrl) CreateOrgPoolManager(ctx context.Context, org params.O
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching internal config")
|
||||
}
|
||||
poolManager, err := pool.NewOrganizationPoolManager(ctx, org, cfgInternal, providers, store)
|
||||
entity := params.GithubEntity{
|
||||
Owner: org.Name,
|
||||
ID: org.ID,
|
||||
WebhookSecret: org.WebhookSecret,
|
||||
EntityType: params.GithubEntityTypeOrganization,
|
||||
}
|
||||
poolManager, err := pool.NewEntityPoolManager(ctx, entity, cfgInternal, providers, store)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creating org pool manager")
|
||||
}
|
||||
|
|
@ -241,7 +254,14 @@ func (p *poolManagerCtrl) CreateEnterprisePoolManager(ctx context.Context, enter
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching internal config")
|
||||
}
|
||||
poolManager, err := pool.NewEnterprisePoolManager(ctx, enterprise, cfgInternal, providers, store)
|
||||
|
||||
entity := params.GithubEntity{
|
||||
Owner: enterprise.Name,
|
||||
ID: enterprise.ID,
|
||||
WebhookSecret: enterprise.WebhookSecret,
|
||||
EntityType: params.GithubEntityTypeEnterprise,
|
||||
}
|
||||
poolManager, err := pool.NewEntityPoolManager(ctx, entity, cfgInternal, providers, store)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "creating enterprise pool manager")
|
||||
}
|
||||
|
|
|
|||
417
util/util.go
417
util/util.go
|
|
@ -16,73 +16,438 @@ package util
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
runnerErrors "github.com/cloudbase/garm-provider-common/errors"
|
||||
"github.com/cloudbase/garm/metrics"
|
||||
"github.com/cloudbase/garm/params"
|
||||
"github.com/cloudbase/garm/runner/common"
|
||||
)
|
||||
|
||||
type githubClient struct {
|
||||
*github.ActionsService
|
||||
org *github.OrganizationsService
|
||||
repo *github.RepositoriesService
|
||||
org *github.OrganizationsService
|
||||
repo *github.RepositoriesService
|
||||
enterprise *github.EnterpriseService
|
||||
|
||||
entity params.GithubEntity
|
||||
}
|
||||
|
||||
func (g *githubClient) ListOrgHooks(ctx context.Context, org string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
return g.org.ListHooks(ctx, org, opts)
|
||||
func (g *githubClient) ListEntityHooks(ctx context.Context, opts *github.ListOptions) (ret []*github.Hook, response *github.Response, err error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListHooks", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListHooks", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
}
|
||||
}()
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
ret, response, err = g.repo.ListHooks(ctx, g.entity.Owner, g.entity.Name, opts)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
ret, response, err = g.org.ListHooks(ctx, g.entity.Owner, opts)
|
||||
default:
|
||||
return nil, nil, errors.New("invalid entity type")
|
||||
}
|
||||
return ret, response, err
|
||||
}
|
||||
|
||||
func (g *githubClient) GetOrgHook(ctx context.Context, org string, id int64) (*github.Hook, *github.Response, error) {
|
||||
return g.org.GetHook(ctx, org, id)
|
||||
func (g *githubClient) GetEntityHook(ctx context.Context, id int64) (ret *github.Hook, err error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"GetHook", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"GetHook", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
}
|
||||
}()
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
ret, _, err = g.repo.GetHook(ctx, g.entity.Owner, g.entity.Name, id)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
ret, _, err = g.org.GetHook(ctx, g.entity.Owner, id)
|
||||
default:
|
||||
return nil, errors.New("invalid entity type")
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (g *githubClient) CreateOrgHook(ctx context.Context, org string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
return g.org.CreateHook(ctx, org, hook)
|
||||
func (g *githubClient) CreateEntityHook(ctx context.Context, hook *github.Hook) (ret *github.Hook, err error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"CreateHook", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"CreateHook", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
}
|
||||
}()
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
ret, _, err = g.repo.CreateHook(ctx, g.entity.Owner, g.entity.Name, hook)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
ret, _, err = g.org.CreateHook(ctx, g.entity.Owner, hook)
|
||||
default:
|
||||
return nil, errors.New("invalid entity type")
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (g *githubClient) DeleteOrgHook(ctx context.Context, org string, id int64) (*github.Response, error) {
|
||||
return g.org.DeleteHook(ctx, org, id)
|
||||
func (g *githubClient) DeleteEntityHook(ctx context.Context, id int64) (ret *github.Response, err error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"DeleteHook", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"DeleteHook", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
}
|
||||
}()
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
ret, err = g.repo.DeleteHook(ctx, g.entity.Owner, g.entity.Name, id)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
ret, err = g.org.DeleteHook(ctx, g.entity.Owner, id)
|
||||
default:
|
||||
return nil, errors.New("invalid entity type")
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (g *githubClient) PingOrgHook(ctx context.Context, org string, id int64) (*github.Response, error) {
|
||||
return g.org.PingHook(ctx, org, id)
|
||||
func (g *githubClient) PingEntityHook(ctx context.Context, id int64) (ret *github.Response, err error) {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"PingHook", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"PingHook", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
}
|
||||
}()
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
ret, err = g.repo.PingHook(ctx, g.entity.Owner, g.entity.Name, id)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
ret, err = g.org.PingHook(ctx, g.entity.Owner, id)
|
||||
default:
|
||||
return nil, errors.New("invalid entity type")
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (g *githubClient) ListRepoHooks(ctx context.Context, owner, repo string, opts *github.ListOptions) ([]*github.Hook, *github.Response, error) {
|
||||
return g.repo.ListHooks(ctx, owner, repo, opts)
|
||||
func (g *githubClient) ListEntityRunners(ctx context.Context, opts *github.ListOptions) (*github.Runners, *github.Response, error) {
|
||||
var ret *github.Runners
|
||||
var response *github.Response
|
||||
var err error
|
||||
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListEntityRunners", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListEntityRunners", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
}
|
||||
}()
|
||||
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
ret, response, err = g.ListRunners(ctx, g.entity.Owner, g.entity.Name, opts)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
ret, response, err = g.ListOrganizationRunners(ctx, g.entity.Owner, opts)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
ret, response, err = g.enterprise.ListRunners(ctx, g.entity.Owner, opts)
|
||||
default:
|
||||
return nil, nil, errors.New("invalid entity type")
|
||||
}
|
||||
|
||||
return ret, response, err
|
||||
}
|
||||
|
||||
func (g *githubClient) GetRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Hook, *github.Response, error) {
|
||||
return g.repo.GetHook(ctx, owner, repo, id)
|
||||
func (g *githubClient) ListEntityRunnerApplicationDownloads(ctx context.Context) ([]*github.RunnerApplicationDownload, *github.Response, error) {
|
||||
var ret []*github.RunnerApplicationDownload
|
||||
var response *github.Response
|
||||
var err error
|
||||
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListEntityRunnerApplicationDownloads", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListEntityRunnerApplicationDownloads", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
}
|
||||
}()
|
||||
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
ret, response, err = g.ListRunnerApplicationDownloads(ctx, g.entity.Owner, g.entity.Name)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
ret, response, err = g.ListOrganizationRunnerApplicationDownloads(ctx, g.entity.Owner)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
ret, response, err = g.enterprise.ListRunnerApplicationDownloads(ctx, g.entity.Owner)
|
||||
default:
|
||||
return nil, nil, errors.New("invalid entity type")
|
||||
}
|
||||
|
||||
return ret, response, err
|
||||
}
|
||||
|
||||
func (g *githubClient) CreateRepoHook(ctx context.Context, owner, repo string, hook *github.Hook) (*github.Hook, *github.Response, error) {
|
||||
return g.repo.CreateHook(ctx, owner, repo, hook)
|
||||
func (g *githubClient) RemoveEntityRunner(ctx context.Context, runnerID int64) (*github.Response, error) {
|
||||
var response *github.Response
|
||||
var err error
|
||||
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"RemoveEntityRunner", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"RemoveEntityRunner", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
}
|
||||
}()
|
||||
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
response, err = g.RemoveRunner(ctx, g.entity.Owner, g.entity.Name, runnerID)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
response, err = g.RemoveOrganizationRunner(ctx, g.entity.Owner, runnerID)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
response, err = g.enterprise.RemoveRunner(ctx, g.entity.Owner, runnerID)
|
||||
default:
|
||||
return nil, errors.New("invalid entity type")
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (g *githubClient) DeleteRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Response, error) {
|
||||
return g.repo.DeleteHook(ctx, owner, repo, id)
|
||||
func (g *githubClient) CreateEntityRegistrationToken(ctx context.Context) (*github.RegistrationToken, *github.Response, error) {
|
||||
var ret *github.RegistrationToken
|
||||
var response *github.Response
|
||||
var err error
|
||||
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"CreateEntityRegistrationToken", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"CreateEntityRegistrationToken", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
}
|
||||
}()
|
||||
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
ret, response, err = g.CreateRegistrationToken(ctx, g.entity.Owner, g.entity.Name)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
ret, response, err = g.CreateOrganizationRegistrationToken(ctx, g.entity.Owner)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
ret, response, err = g.enterprise.CreateRegistrationToken(ctx, g.entity.Owner)
|
||||
default:
|
||||
return nil, nil, errors.New("invalid entity type")
|
||||
}
|
||||
|
||||
return ret, response, err
|
||||
}
|
||||
|
||||
func (g *githubClient) PingRepoHook(ctx context.Context, owner, repo string, id int64) (*github.Response, error) {
|
||||
return g.repo.PingHook(ctx, owner, repo, id)
|
||||
func (g *githubClient) getOrganizationRunnerGroupIDByName(ctx context.Context, entity params.GithubEntity, rgName string) (int64, error) {
|
||||
opts := github.ListOrgRunnerGroupOptions{
|
||||
ListOptions: github.ListOptions{
|
||||
PerPage: 100,
|
||||
},
|
||||
}
|
||||
|
||||
for {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListOrganizationRunnerGroups", // label: operation
|
||||
entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
runnerGroups, ghResp, err := g.ListOrganizationRunnerGroups(ctx, entity.Owner, &opts)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListOrganizationRunnerGroups", // label: operation
|
||||
entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return 0, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners")
|
||||
}
|
||||
return 0, errors.Wrap(err, "fetching runners")
|
||||
}
|
||||
for _, runnerGroup := range runnerGroups.RunnerGroups {
|
||||
if runnerGroup.Name != nil && *runnerGroup.Name == rgName {
|
||||
return *runnerGroup.ID, nil
|
||||
}
|
||||
}
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
return 0, runnerErrors.NewNotFoundError("runner group not found")
|
||||
}
|
||||
|
||||
func GithubClient(_ context.Context, credsDetails params.GithubCredentials) (common.GithubClient, common.GithubEnterpriseClient, error) {
|
||||
func (g *githubClient) getEnterpriseRunnerGroupIDByName(ctx context.Context, entity params.GithubEntity, rgName string) (int64, error) {
|
||||
opts := github.ListEnterpriseRunnerGroupOptions{
|
||||
ListOptions: github.ListOptions{
|
||||
PerPage: 100,
|
||||
},
|
||||
}
|
||||
|
||||
for {
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"ListRunnerGroups", // label: operation
|
||||
entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
runnerGroups, ghResp, err := g.enterprise.ListRunnerGroups(ctx, entity.Owner, &opts)
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"ListRunnerGroups", // label: operation
|
||||
entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized {
|
||||
return 0, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners")
|
||||
}
|
||||
return 0, errors.Wrap(err, "fetching runners")
|
||||
}
|
||||
for _, runnerGroup := range runnerGroups.RunnerGroups {
|
||||
if runnerGroup.Name != nil && *runnerGroup.Name == rgName {
|
||||
return *runnerGroup.ID, nil
|
||||
}
|
||||
}
|
||||
if ghResp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opts.Page = ghResp.NextPage
|
||||
}
|
||||
return 0, runnerErrors.NewNotFoundError("runner group not found")
|
||||
}
|
||||
|
||||
func (g *githubClient) GetEntityJITConfig(ctx context.Context, instance string, pool params.Pool, labels []string) (jitConfigMap map[string]string, runner *github.Runner, err error) {
|
||||
var rgID int64
|
||||
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
rgID = 1
|
||||
case params.GithubEntityTypeOrganization:
|
||||
rgID, err = g.getOrganizationRunnerGroupIDByName(ctx, g.entity, pool.GitHubRunnerGroup)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
rgID, err = g.getEnterpriseRunnerGroupIDByName(ctx, g.entity, pool.GitHubRunnerGroup)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("getting runner group ID: %w", err)
|
||||
}
|
||||
|
||||
req := github.GenerateJITConfigRequest{
|
||||
Name: instance,
|
||||
RunnerGroupID: rgID,
|
||||
Labels: labels,
|
||||
// nolint:golangci-lint,godox
|
||||
// TODO(gabriel-samfira): Should we make this configurable?
|
||||
WorkFolder: github.String("_work"),
|
||||
}
|
||||
|
||||
metrics.GithubOperationCount.WithLabelValues(
|
||||
"GetEntityJITConfig", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
|
||||
var ret *github.JITRunnerConfig
|
||||
var response *github.Response
|
||||
|
||||
switch g.entity.EntityType {
|
||||
case params.GithubEntityTypeRepository:
|
||||
ret, response, err = g.GenerateRepoJITConfig(ctx, g.entity.Owner, g.entity.Name, &req)
|
||||
case params.GithubEntityTypeOrganization:
|
||||
ret, response, err = g.GenerateOrgJITConfig(ctx, g.entity.Owner, &req)
|
||||
case params.GithubEntityTypeEnterprise:
|
||||
ret, response, err = g.enterprise.GenerateEnterpriseJITConfig(ctx, g.entity.Owner, &req)
|
||||
}
|
||||
if err != nil {
|
||||
metrics.GithubOperationFailedCount.WithLabelValues(
|
||||
"GetEntityJITConfig", // label: operation
|
||||
g.entity.LabelScope(), // label: scope
|
||||
).Inc()
|
||||
if response != nil && response.StatusCode == http.StatusUnauthorized {
|
||||
return nil, nil, fmt.Errorf("failed to get JIT config: %w", err)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to get JIT config: %w", err)
|
||||
}
|
||||
|
||||
defer func(run *github.Runner) {
|
||||
if err != nil && run != nil {
|
||||
_, innerErr := g.RemoveEntityRunner(ctx, run.GetID())
|
||||
slog.With(slog.Any("error", innerErr)).ErrorContext(
|
||||
ctx, "failed to remove runner",
|
||||
"runner_id", run.GetID(), string(g.entity.EntityType), g.entity.String())
|
||||
}
|
||||
}(ret.Runner)
|
||||
|
||||
decoded, err := base64.StdEncoding.DecodeString(*ret.EncodedJITConfig)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode JIT config: %w", err)
|
||||
}
|
||||
|
||||
var jitConfig map[string]string
|
||||
if err := json.Unmarshal(decoded, &jitConfig); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal JIT config: %w", err)
|
||||
}
|
||||
|
||||
return jitConfig, ret.Runner, nil
|
||||
}
|
||||
|
||||
func GithubClient(_ context.Context, entity params.GithubEntity, credsDetails params.GithubCredentials) (common.GithubClient, error) {
|
||||
if credsDetails.HTTPClient == nil {
|
||||
return nil, nil, errors.New("http client is nil")
|
||||
return nil, errors.New("http client is nil")
|
||||
}
|
||||
ghClient, err := github.NewClient(credsDetails.HTTPClient).WithEnterpriseURLs(credsDetails.APIBaseURL, credsDetails.UploadBaseURL)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "fetching github client")
|
||||
return nil, errors.Wrap(err, "fetching github client")
|
||||
}
|
||||
|
||||
cli := &githubClient{
|
||||
ActionsService: ghClient.Actions,
|
||||
org: ghClient.Organizations,
|
||||
repo: ghClient.Repositories,
|
||||
enterprise: ghClient.Enterprise,
|
||||
entity: entity,
|
||||
}
|
||||
return cli, ghClient.Enterprise, nil
|
||||
return cli, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue