2022-05-05 13:25:50 +00:00
|
|
|
// Copyright 2022 Cloudbase Solutions SRL
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
|
|
|
// not use this file except in compliance with the License. You may obtain
|
|
|
|
|
// a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
|
// License for the specific language governing permissions and limitations
|
|
|
|
|
// under the License.
|
|
|
|
|
|
2022-04-15 15:22:47 +00:00
|
|
|
package params
|
|
|
|
|
|
2022-04-19 20:22:50 +00:00
|
|
|
import (
|
2023-08-28 09:44:18 +00:00
|
|
|
"bytes"
|
2024-04-15 08:32:19 +00:00
|
|
|
"context"
|
|
|
|
|
"crypto/tls"
|
2023-08-28 09:44:18 +00:00
|
|
|
"crypto/x509"
|
2023-01-30 00:40:01 +00:00
|
|
|
"encoding/json"
|
2023-08-28 09:44:18 +00:00
|
|
|
"encoding/pem"
|
|
|
|
|
"fmt"
|
2024-03-01 19:10:30 +00:00
|
|
|
"net/http"
|
2022-04-28 16:13:20 +00:00
|
|
|
"time"
|
2022-04-18 17:26:13 +00:00
|
|
|
|
2024-04-15 08:32:19 +00:00
|
|
|
"github.com/bradleyfalzon/ghinstallation/v2"
|
2023-12-18 16:09:49 +00:00
|
|
|
"github.com/google/go-github/v57/github"
|
2023-04-10 00:03:49 +00:00
|
|
|
"github.com/google/uuid"
|
2024-04-15 08:32:19 +00:00
|
|
|
"golang.org/x/oauth2"
|
2024-02-22 07:31:51 +01:00
|
|
|
|
|
|
|
|
commonParams "github.com/cloudbase/garm-provider-common/params"
|
|
|
|
|
"github.com/cloudbase/garm/util/appdefaults"
|
2022-04-15 15:22:47 +00:00
|
|
|
)
|
|
|
|
|
|
2023-04-19 11:39:55 +02:00
|
|
|
type (
|
2024-03-17 05:59:47 +00:00
|
|
|
GithubEntityType string
|
2023-08-15 17:19:06 +00:00
|
|
|
EventType string
|
|
|
|
|
EventLevel string
|
|
|
|
|
ProviderType string
|
|
|
|
|
JobStatus string
|
|
|
|
|
RunnerStatus string
|
|
|
|
|
WebhookEndpointType string
|
2024-03-02 17:04:27 +00:00
|
|
|
GithubAuthType string
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
PoolBalancerType string
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// PoolBalancerTypeRoundRobin will try to cycle through the pools of an entity
|
|
|
|
|
// in a round robin fashion. For example, if a repository has multiple pools that
|
|
|
|
|
// match a certain set of labels, and the entity is configured to use round robin
|
|
|
|
|
// balancer, the pool manager will attempt to create instances in each pool in turn
|
|
|
|
|
// for each job that needs to be serviced. So job1 in pool1, job2 in pool2 and so on.
|
|
|
|
|
PoolBalancerTypeRoundRobin PoolBalancerType = "roundrobin"
|
2024-03-15 07:26:04 +00:00
|
|
|
// PoolBalancerTypePack will try to create instances in the first pool that matches
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
// the required labels. If the pool is full, it will move on to the next pool and so on.
|
2024-03-15 07:26:04 +00:00
|
|
|
PoolBalancerTypePack PoolBalancerType = "pack"
|
|
|
|
|
// PoolBalancerTypeNone denotes to the default behavior of the pool manager, which is
|
|
|
|
|
// to use the round robin balancer.
|
|
|
|
|
PoolBalancerTypeNone PoolBalancerType = ""
|
2023-04-19 11:39:55 +02:00
|
|
|
)
|
2023-03-14 14:15:10 +02:00
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// LXDProvider represents the LXD provider.
|
|
|
|
|
LXDProvider ProviderType = "lxd"
|
|
|
|
|
// ExternalProvider represents an external provider.
|
|
|
|
|
ExternalProvider ProviderType = "external"
|
|
|
|
|
)
|
2022-04-26 20:29:58 +00:00
|
|
|
|
2023-08-15 17:19:06 +00:00
|
|
|
const (
|
|
|
|
|
// WebhookEndpointDirect instructs garm that it should attempt to create a webhook
|
|
|
|
|
// in the target entity, using the callback URL defined in the config as a target.
|
|
|
|
|
WebhookEndpointDirect WebhookEndpointType = "direct"
|
|
|
|
|
// WebhookEndpointTunnel instructs garm that it should attempt to create a webhook
|
|
|
|
|
// in the target entity, using the tunnel URL as a base for the webhook URL.
|
|
|
|
|
// This is defined for future use.
|
|
|
|
|
WebhookEndpointTunnel WebhookEndpointType = "tunnel"
|
|
|
|
|
)
|
|
|
|
|
|
2023-04-10 00:03:49 +00:00
|
|
|
const (
|
|
|
|
|
JobStatusQueued JobStatus = "queued"
|
|
|
|
|
JobStatusInProgress JobStatus = "in_progress"
|
|
|
|
|
JobStatusCompleted JobStatus = "completed"
|
|
|
|
|
)
|
|
|
|
|
|
2023-01-20 17:08:15 +02:00
|
|
|
const (
|
2024-03-17 05:59:47 +00:00
|
|
|
GithubEntityTypeRepository GithubEntityType = "repository"
|
|
|
|
|
GithubEntityTypeOrganization GithubEntityType = "organization"
|
|
|
|
|
GithubEntityTypeEnterprise GithubEntityType = "enterprise"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
MetricsLabelEnterpriseScope = "Enterprise"
|
|
|
|
|
MetricsLabelRepositoryScope = "Repository"
|
|
|
|
|
MetricsLabelOrganizationScope = "Organization"
|
2023-01-20 17:08:15 +02:00
|
|
|
)
|
|
|
|
|
|
2022-12-29 16:49:50 +00:00
|
|
|
const (
|
|
|
|
|
StatusEvent EventType = "status"
|
|
|
|
|
FetchTokenEvent EventType = "fetchToken"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
EventInfo EventLevel = "info"
|
|
|
|
|
EventWarning EventLevel = "warning"
|
|
|
|
|
EventError EventLevel = "error"
|
|
|
|
|
)
|
|
|
|
|
|
2023-03-14 14:15:10 +02:00
|
|
|
const (
|
2023-07-21 15:30:22 +00:00
|
|
|
RunnerIdle RunnerStatus = "idle"
|
|
|
|
|
RunnerPending RunnerStatus = "pending"
|
|
|
|
|
RunnerTerminated RunnerStatus = "terminated"
|
|
|
|
|
RunnerInstalling RunnerStatus = "installing"
|
|
|
|
|
RunnerFailed RunnerStatus = "failed"
|
|
|
|
|
RunnerActive RunnerStatus = "active"
|
2023-03-14 14:15:10 +02:00
|
|
|
)
|
|
|
|
|
|
2024-03-02 17:04:27 +00:00
|
|
|
const (
|
|
|
|
|
// GithubAuthTypePAT is the OAuth token based authentication
|
|
|
|
|
GithubAuthTypePAT GithubAuthType = "pat"
|
|
|
|
|
// GithubAuthTypeApp is the GitHub App based authentication
|
|
|
|
|
GithubAuthTypeApp GithubAuthType = "app"
|
|
|
|
|
)
|
|
|
|
|
|
2024-03-17 05:59:47 +00:00
|
|
|
func (e GithubEntityType) String() string {
|
|
|
|
|
return string(e)
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-03 19:49:14 +00:00
|
|
|
type StatusMessage struct {
|
2022-12-29 16:49:50 +00:00
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
EventType EventType `json:"event_type"`
|
|
|
|
|
EventLevel EventLevel `json:"event_level"`
|
2022-05-03 19:49:14 +00:00
|
|
|
}
|
|
|
|
|
|
2022-04-15 15:22:47 +00:00
|
|
|
type Instance struct {
|
2022-04-26 20:29:58 +00:00
|
|
|
// ID is the database ID of this instance.
|
2022-05-10 12:28:39 +00:00
|
|
|
ID string `json:"id,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
2022-04-15 15:22:47 +00:00
|
|
|
// PeoviderID is the unique ID the provider associated
|
|
|
|
|
// with the compute instance. We use this to identify the
|
|
|
|
|
// instance in the provider.
|
|
|
|
|
ProviderID string `json:"provider_id,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
2022-06-29 16:23:01 +00:00
|
|
|
// AgentID is the github runner agent ID.
|
|
|
|
|
AgentID int64 `json:"agent_id"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
2022-04-15 15:22:47 +00:00
|
|
|
// Name is the name associated with an instance. Depending on
|
|
|
|
|
// the provider, this may or may not be useful in the context of
|
|
|
|
|
// the provider, but we can use it internally to identify the
|
|
|
|
|
// instance.
|
|
|
|
|
Name string `json:"name,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
2022-04-15 15:22:47 +00:00
|
|
|
// OSType is the operating system type. For now, only Linux and
|
|
|
|
|
// Windows are supported.
|
2023-07-21 15:30:22 +00:00
|
|
|
OSType commonParams.OSType `json:"os_type,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
2022-04-19 20:22:50 +00:00
|
|
|
// OSName is the name of the OS. Eg: ubuntu, centos, etc.
|
2022-04-26 20:29:58 +00:00
|
|
|
OSName string `json:"os_name,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
2022-04-15 15:22:47 +00:00
|
|
|
// OSVersion is the version of the operating system.
|
2022-04-26 20:29:58 +00:00
|
|
|
OSVersion string `json:"os_version,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
2022-04-15 15:22:47 +00:00
|
|
|
// OSArch is the operating system architecture.
|
2023-07-21 15:30:22 +00:00
|
|
|
OSArch commonParams.OSArch `json:"os_arch,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
2022-04-15 15:22:47 +00:00
|
|
|
// Addresses is a list of IP addresses the provider reports
|
|
|
|
|
// for this instance.
|
2023-07-21 15:30:22 +00:00
|
|
|
Addresses []commonParams.Address `json:"addresses,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
2022-04-19 20:22:50 +00:00
|
|
|
// Status is the status of the instance inside the provider (eg: running, stopped, etc)
|
2023-07-21 15:30:22 +00:00
|
|
|
Status commonParams.InstanceStatus `json:"status,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
|
|
|
|
// RunnerStatus is the github runner status as it appears on GitHub.
|
2023-07-21 15:30:22 +00:00
|
|
|
RunnerStatus RunnerStatus `json:"runner_status,omitempty"`
|
2022-04-26 20:29:58 +00:00
|
|
|
|
2023-03-27 08:40:22 +00:00
|
|
|
// PoolID is the ID of the garm pool to which a runner belongs.
|
|
|
|
|
PoolID string `json:"pool_id,omitempty"`
|
|
|
|
|
|
|
|
|
|
// ProviderFault holds any error messages captured from the IaaS provider that is
|
|
|
|
|
// responsible for managing the lifecycle of the runner.
|
|
|
|
|
ProviderFault []byte `json:"provider_fault,omitempty"`
|
|
|
|
|
|
|
|
|
|
// StatusMessages is a list of status messages sent back by the runner as it sets itself
|
|
|
|
|
// up.
|
2022-05-03 19:49:14 +00:00
|
|
|
StatusMessages []StatusMessage `json:"status_messages,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
|
|
|
|
// UpdatedAt is the timestamp of the last update to this runner.
|
|
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
|
|
|
|
|
|
|
// GithubRunnerGroup is the github runner group to which the runner belongs.
|
|
|
|
|
// The runner group must be created by someone with access to the enterprise.
|
|
|
|
|
GitHubRunnerGroup string `json:"github-runner-group"`
|
2022-05-03 19:49:14 +00:00
|
|
|
|
2024-03-11 15:41:38 +00:00
|
|
|
// Job is the current job that is being serviced by this runner.
|
|
|
|
|
Job *Job `json:"job,omitempty"`
|
|
|
|
|
|
2022-04-26 20:29:58 +00:00
|
|
|
// Do not serialize sensitive info.
|
2023-08-18 06:09:44 +00:00
|
|
|
CallbackURL string `json:"-"`
|
|
|
|
|
MetadataURL string `json:"-"`
|
|
|
|
|
CreateAttempt int `json:"-"`
|
|
|
|
|
TokenFetched bool `json:"-"`
|
|
|
|
|
AditionalLabels []string `json:"-"`
|
|
|
|
|
JitConfiguration map[string]string `json:"-"`
|
2022-04-15 15:22:47 +00:00
|
|
|
}
|
2022-04-18 17:26:13 +00:00
|
|
|
|
2022-12-04 17:30:27 +00:00
|
|
|
func (i Instance) GetName() string {
|
|
|
|
|
return i.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (i Instance) GetID() string {
|
|
|
|
|
return i.ID
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-05 13:10:59 +03:00
|
|
|
// used by swagger client generated code
|
|
|
|
|
type Instances []Instance
|
|
|
|
|
|
2022-04-18 17:26:13 +00:00
|
|
|
type BootstrapInstance struct {
|
2022-04-26 20:29:58 +00:00
|
|
|
Name string `json:"name"`
|
2022-04-18 17:26:13 +00:00
|
|
|
Tools []*github.RunnerApplicationDownload `json:"tools"`
|
|
|
|
|
// RepoURL is the URL the github runner agent needs to configure itself.
|
|
|
|
|
RepoURL string `json:"repo_url"`
|
|
|
|
|
// CallbackUrl is the URL where the instance can send a post, signaling
|
|
|
|
|
// progress or status.
|
2022-04-19 20:22:50 +00:00
|
|
|
CallbackURL string `json:"callback-url"`
|
2022-12-02 19:48:32 +00:00
|
|
|
// MetadataURL is the URL where instances can fetch information needed to set themselves up.
|
|
|
|
|
MetadataURL string `json:"metadata-url"`
|
2022-04-18 17:26:13 +00:00
|
|
|
// InstanceToken is the token that needs to be set by the instance in the headers
|
2022-05-04 11:44:10 +00:00
|
|
|
// in order to send updated back to the garm via CallbackURL.
|
2022-04-19 20:22:50 +00:00
|
|
|
InstanceToken string `json:"instance-token"`
|
2022-04-18 17:26:13 +00:00
|
|
|
// SSHKeys are the ssh public keys we may want to inject inside the runners, if the
|
|
|
|
|
// provider supports it.
|
2022-04-19 20:22:50 +00:00
|
|
|
SSHKeys []string `json:"ssh-keys"`
|
2023-01-30 00:40:01 +00:00
|
|
|
// ExtraSpecs is an opaque raw json that gets sent to the provider
|
|
|
|
|
// as part of the bootstrap params for instances. It can contain
|
|
|
|
|
// any kind of data needed by providers. The contents of this field means
|
|
|
|
|
// nothing to garm itself. We don't act on the information in this field at
|
|
|
|
|
// all. We only validate that it's a proper json.
|
|
|
|
|
ExtraSpecs json.RawMessage `json:"extra_specs,omitempty"`
|
2022-04-22 14:46:27 +00:00
|
|
|
|
2023-03-27 08:40:22 +00:00
|
|
|
// GitHubRunnerGroup is the github runner group in which the newly installed runner
|
|
|
|
|
// should be added to. The runner group must be created by someone with access to the
|
|
|
|
|
// enterprise.
|
|
|
|
|
GitHubRunnerGroup string `json:"github-runner-group"`
|
|
|
|
|
|
|
|
|
|
// CACertBundle is a CA certificate bundle which will be sent to instances and which
|
|
|
|
|
// will tipically be installed as a system wide trusted root CA. by either cloud-init
|
|
|
|
|
// or whatever mechanism the provider will use to set up the runner.
|
2022-10-12 21:45:07 +00:00
|
|
|
CACertBundle []byte `json:"ca-cert-bundle"`
|
|
|
|
|
|
2023-03-27 08:40:22 +00:00
|
|
|
// OSArch is the target OS CPU architecture of the runner.
|
2023-07-21 15:30:22 +00:00
|
|
|
OSArch commonParams.OSArch `json:"arch"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
|
|
|
|
// OSType is the target OS platform of the runner (windows, linux).
|
2023-07-21 15:30:22 +00:00
|
|
|
OSType commonParams.OSType `json:"os_type"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
|
|
|
|
// Flavor is the platform specific abstraction that defines what resources will be allocated
|
|
|
|
|
// to the runner (CPU, RAM, disk space, etc). This field is meaningful to the provider which
|
|
|
|
|
// handles the actual creation.
|
|
|
|
|
Flavor string `json:"flavor"`
|
|
|
|
|
|
|
|
|
|
// Image is the platform specific identifier of the operating system template that will be used
|
|
|
|
|
// to spin up a new machine.
|
|
|
|
|
Image string `json:"image"`
|
|
|
|
|
|
|
|
|
|
// Labels are a list of github runner labels that will be added to the runner.
|
2023-03-14 14:15:10 +02:00
|
|
|
Labels []string `json:"labels"`
|
2023-03-27 08:40:22 +00:00
|
|
|
|
|
|
|
|
// PoolID is the ID of the garm pool to which this runner belongs.
|
|
|
|
|
PoolID string `json:"pool_id"`
|
2023-04-19 11:40:51 +02:00
|
|
|
|
|
|
|
|
// UserDataOptions are the options for the user data generation.
|
|
|
|
|
UserDataOptions UserDataOptions `json:"user_data_options"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type UserDataOptions struct {
|
2023-06-11 17:30:16 +02:00
|
|
|
DisableUpdatesOnBoot bool `json:"disable_updates_on_boot"`
|
|
|
|
|
ExtraPackages []string `json:"extra_packages"`
|
2022-04-18 17:26:13 +00:00
|
|
|
}
|
2022-04-25 00:03:26 +00:00
|
|
|
|
|
|
|
|
type Tag struct {
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Pool struct {
|
2023-01-20 12:05:32 +02:00
|
|
|
RunnerPrefix
|
|
|
|
|
|
2023-07-21 15:30:22 +00:00
|
|
|
ID string `json:"id"`
|
|
|
|
|
ProviderName string `json:"provider_name"`
|
|
|
|
|
MaxRunners uint `json:"max_runners"`
|
|
|
|
|
MinIdleRunners uint `json:"min_idle_runners"`
|
|
|
|
|
Image string `json:"image"`
|
|
|
|
|
Flavor string `json:"flavor"`
|
|
|
|
|
OSType commonParams.OSType `json:"os_type"`
|
|
|
|
|
OSArch commonParams.OSArch `json:"os_arch"`
|
|
|
|
|
Tags []Tag `json:"tags"`
|
|
|
|
|
Enabled bool `json:"enabled"`
|
|
|
|
|
Instances []Instance `json:"instances"`
|
|
|
|
|
RepoID string `json:"repo_id,omitempty"`
|
|
|
|
|
RepoName string `json:"repo_name,omitempty"`
|
|
|
|
|
OrgID string `json:"org_id,omitempty"`
|
|
|
|
|
OrgName string `json:"org_name,omitempty"`
|
|
|
|
|
EnterpriseID string `json:"enterprise_id,omitempty"`
|
|
|
|
|
EnterpriseName string `json:"enterprise_name,omitempty"`
|
|
|
|
|
RunnerBootstrapTimeout uint `json:"runner_bootstrap_timeout"`
|
2023-01-30 00:40:01 +00:00
|
|
|
// ExtraSpecs is an opaque raw json that gets sent to the provider
|
|
|
|
|
// as part of the bootstrap params for instances. It can contain
|
|
|
|
|
// any kind of data needed by providers. The contents of this field means
|
|
|
|
|
// nothing to garm itself. We don't act on the information in this field at
|
|
|
|
|
// all. We only validate that it's a proper json.
|
|
|
|
|
ExtraSpecs json.RawMessage `json:"extra_specs,omitempty"`
|
2023-03-27 08:40:22 +00:00
|
|
|
// GithubRunnerGroup is the github runner group in which the runners will be added.
|
|
|
|
|
// The runner group must be created by someone with access to the enterprise.
|
|
|
|
|
GitHubRunnerGroup string `json:"github-runner-group"`
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
|
|
|
|
|
// Priority is the priority of the pool. The higher the number, the higher the priority.
|
|
|
|
|
// When fetching matching pools for a set of tags, the result will be sorted in descending
|
|
|
|
|
// order of priority.
|
|
|
|
|
Priority uint `json:"priority"`
|
2022-06-29 23:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-29 18:18:29 +00:00
|
|
|
func (p Pool) GithubEntity() (GithubEntity, error) {
|
|
|
|
|
switch p.PoolType() {
|
|
|
|
|
case GithubEntityTypeRepository:
|
|
|
|
|
return GithubEntity{
|
|
|
|
|
ID: p.RepoID,
|
|
|
|
|
EntityType: GithubEntityTypeRepository,
|
|
|
|
|
}, nil
|
|
|
|
|
case GithubEntityTypeOrganization:
|
|
|
|
|
return GithubEntity{
|
|
|
|
|
ID: p.OrgID,
|
|
|
|
|
EntityType: GithubEntityTypeOrganization,
|
|
|
|
|
}, nil
|
|
|
|
|
case GithubEntityTypeEnterprise:
|
|
|
|
|
return GithubEntity{
|
|
|
|
|
ID: p.EnterpriseID,
|
|
|
|
|
EntityType: GithubEntityTypeEnterprise,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
return GithubEntity{}, fmt.Errorf("pool has no associated entity")
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 17:30:27 +00:00
|
|
|
func (p Pool) GetID() string {
|
|
|
|
|
return p.ID
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-29 23:44:03 +00:00
|
|
|
func (p *Pool) RunnerTimeout() uint {
|
|
|
|
|
if p.RunnerBootstrapTimeout == 0 {
|
2023-03-14 14:15:10 +02:00
|
|
|
return appdefaults.DefaultRunnerBootstrapTimeout
|
2022-06-29 23:44:03 +00:00
|
|
|
}
|
|
|
|
|
return p.RunnerBootstrapTimeout
|
2022-04-25 00:03:26 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-17 05:59:47 +00:00
|
|
|
func (p *Pool) PoolType() GithubEntityType {
|
2024-02-22 10:50:57 +01:00
|
|
|
switch {
|
|
|
|
|
case p.RepoID != "":
|
2024-03-17 05:59:47 +00:00
|
|
|
return GithubEntityTypeRepository
|
2024-02-22 10:50:57 +01:00
|
|
|
case p.OrgID != "":
|
2024-03-17 05:59:47 +00:00
|
|
|
return GithubEntityTypeOrganization
|
2024-02-22 10:50:57 +01:00
|
|
|
case p.EnterpriseID != "":
|
2024-03-17 05:59:47 +00:00
|
|
|
return GithubEntityTypeEnterprise
|
2023-01-20 17:08:15 +02:00
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
Prevent abusing the GH API
On large deployments with many jobs, we cannot check each job that
we recorded in the DB against the GH API.
Before this change, if a job was updated more than 10 minutes ago,
garm would check against the GH api if that job still existed. While
this approach allowed us to maintain a consistent view over which jobs
still exist and which are stale, it had the potential of spamming the
GH API, leading to rate limiting.
This change uses the scale-down loop as an indicator for job staleness.
If a job remains in queued state in our DB, but has dissapeared from GH
or was serviced by another runner and we never got the hook (garm was down
or GH had an issue - happened in the past), then garm will spin up a new
runner for it. If that runner or any other runner is scaled down, we check
if we have jobs in the queue that should have matched that runner. If we did,
there is a high chance that the job no longer exists in GH and we can remove
the job from the queue.
Of course, there is a chance that GH is having issues and the job is never
pushed to the runner, but we can't really account for everything. In this case
I'd rather avoid rate limiting ourselves.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2023-12-15 22:33:22 +00:00
|
|
|
func (p *Pool) HasRequiredLabels(set []string) bool {
|
|
|
|
|
asMap := make(map[string]struct{}, len(p.Tags))
|
|
|
|
|
for _, t := range p.Tags {
|
|
|
|
|
asMap[t.Name] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, l := range set {
|
|
|
|
|
if _, ok := asMap[l]; !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-05 13:10:59 +03:00
|
|
|
// used by swagger client generated code
|
|
|
|
|
type Pools []Pool
|
|
|
|
|
|
2022-04-26 20:29:58 +00:00
|
|
|
type Internal struct {
|
2023-08-15 17:19:06 +00:00
|
|
|
ControllerID string `json:"controller_id"`
|
|
|
|
|
InstanceCallbackURL string `json:"instance_callback_url"`
|
|
|
|
|
InstanceMetadataURL string `json:"instance_metadata_url"`
|
|
|
|
|
BaseWebhookURL string `json:"base_webhook_url"`
|
|
|
|
|
ControllerWebhookURL string `json:"controller_webhook_url"`
|
|
|
|
|
|
|
|
|
|
JWTSecret string `json:"jwt_secret"`
|
2022-10-12 21:45:07 +00:00
|
|
|
// GithubCredentialsDetails contains all info about the credentials, except the
|
|
|
|
|
// token, which is added above.
|
|
|
|
|
GithubCredentialsDetails GithubCredentials `json:"gh_creds_details"`
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
PoolBalancerType PoolBalancerType `json:"pool_balancing_type"`
|
2022-04-26 20:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
2022-04-25 00:03:26 +00:00
|
|
|
type Repository struct {
|
2022-10-20 17:22:47 +03:00
|
|
|
ID string `json:"id"`
|
|
|
|
|
Owner string `json:"owner"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Pools []Pool `json:"pool,omitempty"`
|
|
|
|
|
CredentialsName string `json:"credentials_name"`
|
2024-04-15 08:32:19 +00:00
|
|
|
Credentials GithubCredentials `json:"credentials"`
|
2022-10-20 17:22:47 +03:00
|
|
|
PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"`
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
PoolBalancerType PoolBalancerType `json:"pool_balancing_type"`
|
2024-04-17 12:10:00 +00:00
|
|
|
Endpoint GithubEndpoint `json:"endpoint"`
|
2022-04-26 20:29:58 +00:00
|
|
|
// Do not serialize sensitive info.
|
2022-08-10 12:15:12 +03:00
|
|
|
WebhookSecret string `json:"-"`
|
2022-04-25 00:03:26 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-29 18:18:29 +00:00
|
|
|
func (r Repository) GetEntity() (GithubEntity, error) {
|
|
|
|
|
if r.ID == "" {
|
|
|
|
|
return GithubEntity{}, fmt.Errorf("repository has no ID")
|
|
|
|
|
}
|
|
|
|
|
return GithubEntity{
|
|
|
|
|
ID: r.ID,
|
|
|
|
|
EntityType: GithubEntityTypeRepository,
|
|
|
|
|
Owner: r.Owner,
|
|
|
|
|
Name: r.Name,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 17:30:27 +00:00
|
|
|
func (r Repository) GetName() string {
|
|
|
|
|
return r.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r Repository) GetID() string {
|
|
|
|
|
return r.ID
|
|
|
|
|
}
|
|
|
|
|
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
func (r Repository) GetBalancerType() PoolBalancerType {
|
|
|
|
|
if r.PoolBalancerType == "" {
|
|
|
|
|
return PoolBalancerTypeRoundRobin
|
|
|
|
|
}
|
|
|
|
|
return r.PoolBalancerType
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-30 19:02:20 +03:00
|
|
|
// used by swagger client generated code
|
|
|
|
|
type Repositories []Repository
|
|
|
|
|
|
2022-04-25 00:03:26 +00:00
|
|
|
type Organization struct {
|
2022-10-20 17:22:47 +03:00
|
|
|
ID string `json:"id"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Pools []Pool `json:"pool,omitempty"`
|
|
|
|
|
CredentialsName string `json:"credentials_name"`
|
2024-04-15 08:32:19 +00:00
|
|
|
Credentials GithubCredentials `json:"credentials"`
|
2022-10-20 17:22:47 +03:00
|
|
|
PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"`
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
PoolBalancerType PoolBalancerType `json:"pool_balancing_type"`
|
2024-04-17 12:10:00 +00:00
|
|
|
Endpoint GithubEndpoint `json:"endpoint"`
|
2022-04-26 20:29:58 +00:00
|
|
|
// Do not serialize sensitive info.
|
2022-08-10 12:15:12 +03:00
|
|
|
WebhookSecret string `json:"-"`
|
2022-04-25 00:03:26 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-29 18:18:29 +00:00
|
|
|
func (o Organization) GetEntity() (GithubEntity, error) {
|
|
|
|
|
if o.ID == "" {
|
|
|
|
|
return GithubEntity{}, fmt.Errorf("organization has no ID")
|
|
|
|
|
}
|
|
|
|
|
return GithubEntity{
|
|
|
|
|
ID: o.ID,
|
|
|
|
|
EntityType: GithubEntityTypeOrganization,
|
|
|
|
|
Owner: o.Name,
|
|
|
|
|
WebhookSecret: o.WebhookSecret,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 17:30:27 +00:00
|
|
|
func (o Organization) GetName() string {
|
|
|
|
|
return o.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o Organization) GetID() string {
|
|
|
|
|
return o.ID
|
|
|
|
|
}
|
|
|
|
|
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
func (o Organization) GetBalancerType() PoolBalancerType {
|
|
|
|
|
if o.PoolBalancerType == "" {
|
|
|
|
|
return PoolBalancerTypeRoundRobin
|
|
|
|
|
}
|
|
|
|
|
return o.PoolBalancerType
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-10 17:16:17 +03:00
|
|
|
// used by swagger client generated code
|
|
|
|
|
type Organizations []Organization
|
|
|
|
|
|
2022-10-13 16:09:28 +00:00
|
|
|
type Enterprise struct {
|
2022-10-20 17:22:47 +03:00
|
|
|
ID string `json:"id"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Pools []Pool `json:"pool,omitempty"`
|
|
|
|
|
CredentialsName string `json:"credentials_name"`
|
2024-04-15 08:32:19 +00:00
|
|
|
Credentials GithubCredentials `json:"credentials"`
|
2022-10-20 17:22:47 +03:00
|
|
|
PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"`
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
PoolBalancerType PoolBalancerType `json:"pool_balancing_type"`
|
2024-04-17 12:10:00 +00:00
|
|
|
Endpoint GithubEndpoint `json:"endpoint"`
|
2022-10-13 16:09:28 +00:00
|
|
|
// Do not serialize sensitive info.
|
|
|
|
|
WebhookSecret string `json:"-"`
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-29 18:18:29 +00:00
|
|
|
func (e Enterprise) GetEntity() (GithubEntity, error) {
|
|
|
|
|
if e.ID == "" {
|
|
|
|
|
return GithubEntity{}, fmt.Errorf("enterprise has no ID")
|
|
|
|
|
}
|
|
|
|
|
return GithubEntity{
|
|
|
|
|
ID: e.ID,
|
|
|
|
|
EntityType: GithubEntityTypeEnterprise,
|
|
|
|
|
Owner: e.Name,
|
|
|
|
|
WebhookSecret: e.WebhookSecret,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 17:30:27 +00:00
|
|
|
func (e Enterprise) GetName() string {
|
|
|
|
|
return e.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e Enterprise) GetID() string {
|
|
|
|
|
return e.ID
|
|
|
|
|
}
|
|
|
|
|
|
Add pool balancing strategy
This change adds the ability to specify the pool balancing strategy to
use when processing queued jobs. Before this change, GARM would round-robin
through all pools that matched the set of tags requested by queued jobs.
When round-robin (default) is used for an entity (repo, org or enterprise)
and you have 2 pools defined for that entity with a common set of tags that
match 10 jobs (for example), then those jobs would trigger the creation of
a new runner in each of the two pools in turn. Job 1 would go to pool 1,
job 2 would go to pool 2, job 3 to pool 1, job 4 to pool 2 and so on.
When "stack" is used, those same 10 jobs would trigger the creation of a
new runner in the pool with the highest priority, every time.
In both cases, if a pool is full, the next one would be tried automatically.
For the stack case, this would mean that if pool 2 had a priority of 10 and
pool 1 would have a priority of 5, pool 2 would be saturated first, then
pool 1.
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-03-14 20:04:34 +00:00
|
|
|
func (e Enterprise) GetBalancerType() PoolBalancerType {
|
|
|
|
|
if e.PoolBalancerType == "" {
|
|
|
|
|
return PoolBalancerTypeRoundRobin
|
|
|
|
|
}
|
|
|
|
|
return e.PoolBalancerType
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-18 20:34:09 +03:00
|
|
|
// used by swagger client generated code
|
|
|
|
|
type Enterprises []Enterprise
|
|
|
|
|
|
2022-04-28 16:13:20 +00:00
|
|
|
// Users holds information about a particular user
|
|
|
|
|
type User struct {
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
|
|
Email string `json:"email"`
|
|
|
|
|
Username string `json:"username"`
|
|
|
|
|
FullName string `json:"full_name"`
|
|
|
|
|
Password string `json:"-"`
|
|
|
|
|
Enabled bool `json:"enabled"`
|
|
|
|
|
IsAdmin bool `json:"is_admin"`
|
2022-04-25 00:03:26 +00:00
|
|
|
}
|
2022-04-26 20:29:58 +00:00
|
|
|
|
2022-04-28 16:13:20 +00:00
|
|
|
// JWTResponse holds the JWT token returned as a result of a
|
|
|
|
|
// successful auth
|
|
|
|
|
type JWTResponse struct {
|
|
|
|
|
Token string `json:"token"`
|
|
|
|
|
}
|
2022-04-26 20:29:58 +00:00
|
|
|
|
2022-04-28 16:13:20 +00:00
|
|
|
type ControllerInfo struct {
|
2023-08-15 17:19:06 +00:00
|
|
|
ControllerID uuid.UUID `json:"controller_id"`
|
|
|
|
|
Hostname string `json:"hostname"`
|
|
|
|
|
MetadataURL string `json:"metadata_url"`
|
|
|
|
|
CallbackURL string `json:"callback_url"`
|
|
|
|
|
WebhookURL string `json:"webhook_url"`
|
|
|
|
|
ControllerWebhookURL string `json:"controller_webhook_url"`
|
2023-08-13 06:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
2022-04-28 16:13:20 +00:00
|
|
|
type GithubCredentials struct {
|
2024-04-15 08:32:19 +00:00
|
|
|
ID uint `json:"id"`
|
2024-03-02 17:04:27 +00:00
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
|
Description string `json:"description,omitempty"`
|
|
|
|
|
APIBaseURL string `json:"api_base_url"`
|
|
|
|
|
UploadBaseURL string `json:"upload_base_url"`
|
|
|
|
|
BaseURL string `json:"base_url"`
|
|
|
|
|
CABundle []byte `json:"ca_bundle,omitempty"`
|
|
|
|
|
AuthType GithubAuthType `toml:"auth_type" json:"auth-type"`
|
2024-04-15 08:32:19 +00:00
|
|
|
|
|
|
|
|
Repositories []Repository `json:"repositories,omitempty"`
|
|
|
|
|
Organizations []Organization `json:"organizations,omitempty"`
|
|
|
|
|
Enterprises []Enterprise `json:"enterprises,omitempty"`
|
2024-04-18 16:50:46 +00:00
|
|
|
Endpoint GithubEndpoint `json:"endpoint"`
|
2024-04-15 08:32:19 +00:00
|
|
|
|
2024-04-17 08:05:06 +00:00
|
|
|
CredentialsPayload []byte `json:"-"`
|
2024-04-15 08:32:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (g GithubCredentials) GetHTTPClient(ctx context.Context) (*http.Client, error) {
|
|
|
|
|
var roots *x509.CertPool
|
|
|
|
|
if g.CABundle != nil {
|
|
|
|
|
roots = x509.NewCertPool()
|
|
|
|
|
ok := roots.AppendCertsFromPEM(g.CABundle)
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, fmt.Errorf("failed to parse CA cert")
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-17 08:05:06 +00:00
|
|
|
|
2024-04-15 08:32:19 +00:00
|
|
|
httpTransport := &http.Transport{
|
|
|
|
|
TLSClientConfig: &tls.Config{
|
2024-04-17 08:05:06 +00:00
|
|
|
RootCAs: roots,
|
|
|
|
|
MinVersion: tls.VersionTLS12,
|
2024-04-15 08:32:19 +00:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tc *http.Client
|
|
|
|
|
switch g.AuthType {
|
|
|
|
|
case GithubAuthTypeApp:
|
|
|
|
|
var app GithubApp
|
|
|
|
|
if err := json.Unmarshal(g.CredentialsPayload, &app); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to unmarshal github app credentials: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if app.AppID == 0 || app.InstallationID == 0 || len(app.PrivateKeyBytes) == 0 {
|
|
|
|
|
return nil, fmt.Errorf("github app credentials are missing required fields")
|
|
|
|
|
}
|
|
|
|
|
itr, err := ghinstallation.New(httpTransport, app.AppID, app.InstallationID, app.PrivateKeyBytes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to create github app installation transport: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tc = &http.Client{Transport: itr}
|
|
|
|
|
default:
|
|
|
|
|
var pat GithubPAT
|
|
|
|
|
if err := json.Unmarshal(g.CredentialsPayload, &pat); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to unmarshal github app credentials: %w", err)
|
|
|
|
|
}
|
|
|
|
|
httpClient := &http.Client{Transport: httpTransport}
|
|
|
|
|
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
|
|
|
|
|
|
|
|
|
|
if pat.OAuth2Token == "" {
|
|
|
|
|
return nil, fmt.Errorf("github credentials are missing the OAuth2 token")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ts := oauth2.StaticTokenSource(
|
|
|
|
|
&oauth2.Token{AccessToken: pat.OAuth2Token},
|
|
|
|
|
)
|
|
|
|
|
tc = oauth2.NewClient(ctx, ts)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tc, nil
|
2022-04-27 16:56:28 +00:00
|
|
|
}
|
2022-04-28 16:13:20 +00:00
|
|
|
|
2023-08-28 09:44:18 +00:00
|
|
|
func (g GithubCredentials) RootCertificateBundle() (CertificateBundle, error) {
|
|
|
|
|
if len(g.CABundle) == 0 {
|
|
|
|
|
return CertificateBundle{}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret := map[string][]byte{}
|
|
|
|
|
|
|
|
|
|
var block *pem.Block
|
2024-02-22 10:56:33 +01:00
|
|
|
rest := g.CABundle
|
2023-08-28 09:44:18 +00:00
|
|
|
for {
|
|
|
|
|
block, rest = pem.Decode(rest)
|
|
|
|
|
if block == nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
pub, err := x509.ParseCertificate(block.Bytes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return CertificateBundle{}, err
|
|
|
|
|
}
|
|
|
|
|
out := &bytes.Buffer{}
|
|
|
|
|
if err := pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: block.Bytes}); err != nil {
|
|
|
|
|
return CertificateBundle{}, err
|
|
|
|
|
}
|
|
|
|
|
ret[fmt.Sprintf("%d", pub.SerialNumber)] = out.Bytes()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CertificateBundle{
|
|
|
|
|
RootCertificates: ret,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-10 17:16:17 +03:00
|
|
|
// used by swagger client generated code
|
|
|
|
|
type Credentials []GithubCredentials
|
|
|
|
|
|
2022-04-28 16:13:20 +00:00
|
|
|
type Provider struct {
|
2023-03-14 14:15:10 +02:00
|
|
|
Name string `json:"name"`
|
|
|
|
|
ProviderType ProviderType `json:"type"`
|
|
|
|
|
Description string `json:"description"`
|
2022-04-27 16:56:28 +00:00
|
|
|
}
|
2022-05-04 16:27:24 +00:00
|
|
|
|
2023-07-10 17:16:17 +03:00
|
|
|
// used by swagger client generated code
|
|
|
|
|
type Providers []Provider
|
|
|
|
|
|
2022-05-04 16:27:24 +00:00
|
|
|
type UpdatePoolStateParams struct {
|
2023-07-04 22:11:45 +00:00
|
|
|
WebhookSecret string
|
|
|
|
|
InternalConfig *Internal
|
2022-05-04 16:27:24 +00:00
|
|
|
}
|
2022-10-20 17:22:47 +03:00
|
|
|
|
|
|
|
|
type PoolManagerStatus struct {
|
|
|
|
|
IsRunning bool `json:"running"`
|
|
|
|
|
FailureReason string `json:"failure_reason,omitempty"`
|
|
|
|
|
}
|
2022-12-29 16:49:50 +00:00
|
|
|
|
|
|
|
|
type RunnerInfo struct {
|
|
|
|
|
Name string
|
|
|
|
|
Labels []string
|
|
|
|
|
}
|
2023-01-20 12:05:32 +02:00
|
|
|
|
|
|
|
|
type RunnerPrefix struct {
|
|
|
|
|
Prefix string `json:"runner_prefix"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p RunnerPrefix) GetRunnerPrefix() string {
|
|
|
|
|
if p.Prefix == "" {
|
|
|
|
|
return DefaultRunnerPrefix
|
|
|
|
|
}
|
|
|
|
|
return p.Prefix
|
|
|
|
|
}
|
2023-04-10 00:03:49 +00:00
|
|
|
|
|
|
|
|
type Job struct {
|
|
|
|
|
// ID is the ID of the job.
|
|
|
|
|
ID int64 `json:"id"`
|
|
|
|
|
// RunID is the ID of the workflow run. A run may have multiple jobs.
|
2023-06-23 21:15:46 +00:00
|
|
|
RunID int64 `json:"run_id"`
|
2023-04-10 00:03:49 +00:00
|
|
|
// Action is the specific activity that triggered the event.
|
2023-06-23 21:15:46 +00:00
|
|
|
Action string `json:"action"`
|
2023-04-10 00:03:49 +00:00
|
|
|
// Conclusion is the outcome of the job.
|
|
|
|
|
// Possible values: "success", "failure", "neutral", "cancelled", "skipped",
|
|
|
|
|
// "timed_out", "action_required"
|
|
|
|
|
Conclusion string `json:"conclusion"`
|
|
|
|
|
// Status is the phase of the lifecycle that the job is currently in.
|
|
|
|
|
// "queued", "in_progress" and "completed".
|
|
|
|
|
Status string `json:"status"`
|
|
|
|
|
// Name is the name if the job that was triggered.
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
|
|
|
|
|
StartedAt time.Time
|
|
|
|
|
CompletedAt time.Time
|
|
|
|
|
|
|
|
|
|
GithubRunnerID int64 `json:"runner_id"`
|
|
|
|
|
RunnerName string `json:"runner_name"`
|
|
|
|
|
RunnerGroupID int64 `json:"runner_group_id"`
|
|
|
|
|
RunnerGroupName string `json:"runner_group_name"`
|
|
|
|
|
|
|
|
|
|
// repository in which the job was triggered.
|
|
|
|
|
RepositoryName string
|
|
|
|
|
RepositoryOwner string
|
|
|
|
|
|
|
|
|
|
Labels []string
|
|
|
|
|
|
|
|
|
|
// The entity that received the hook.
|
|
|
|
|
//
|
|
|
|
|
// Webhooks may be configured on the repo, the org and/or the enterprise.
|
|
|
|
|
// If we only configure a repo to use garm, we'll only ever receive a
|
|
|
|
|
// webhook from the repo. But if we configure the parent org of the repo and
|
|
|
|
|
// the parent enterprise of the org to use garm, a webhook will be sent for each
|
|
|
|
|
// entity type, in response to one workflow event. Thus, we will get 3 webhooks
|
|
|
|
|
// with the same run_id and job id. Record all involved entities in the same job
|
|
|
|
|
// if we have them configured in garm.
|
2023-06-28 14:50:59 +00:00
|
|
|
RepoID *uuid.UUID `json:"repo_id,omitempty"`
|
|
|
|
|
OrgID *uuid.UUID `json:"org_id,omitempty"`
|
|
|
|
|
EnterpriseID *uuid.UUID `json:"enterprise_id,omitempty"`
|
2023-04-10 00:03:49 +00:00
|
|
|
|
|
|
|
|
LockedBy uuid.UUID
|
|
|
|
|
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
|
|
}
|
2023-07-18 15:19:53 +03:00
|
|
|
|
|
|
|
|
// used by swagger client generated code
|
|
|
|
|
type Jobs []Job
|
2023-08-15 17:19:06 +00:00
|
|
|
|
|
|
|
|
type InstallWebhookParams struct {
|
|
|
|
|
WebhookEndpointType WebhookEndpointType `json:"webhook_endpoint_type"`
|
|
|
|
|
InsecureSSL bool `json:"insecure_ssl"`
|
|
|
|
|
}
|
2023-08-16 09:11:45 +00:00
|
|
|
|
|
|
|
|
type HookInfo struct {
|
|
|
|
|
ID int64 `json:"id"`
|
|
|
|
|
URL string `json:"url"`
|
|
|
|
|
Events []string `json:"events"`
|
|
|
|
|
Active bool `json:"active"`
|
|
|
|
|
InsecureSSL bool `json:"insecure_ssl"`
|
|
|
|
|
}
|
2023-08-28 09:44:18 +00:00
|
|
|
|
|
|
|
|
type CertificateBundle struct {
|
|
|
|
|
RootCertificates map[string][]byte `json:"root_certificates"`
|
|
|
|
|
}
|
2024-01-04 15:23:43 +00:00
|
|
|
|
|
|
|
|
type UpdateSystemInfoParams struct {
|
|
|
|
|
OSName string `json:"os_name,omitempty"`
|
|
|
|
|
OSVersion string `json:"os_version,omitempty"`
|
|
|
|
|
AgentID *int64 `json:"agent_id,omitempty"`
|
|
|
|
|
}
|
2024-03-17 05:59:47 +00:00
|
|
|
|
|
|
|
|
type GithubEntity struct {
|
2024-04-15 08:32:19 +00:00
|
|
|
Owner string `json:"owner"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
EntityType GithubEntityType `json:"entity_type"`
|
|
|
|
|
Credentials GithubCredentials `json:"credentials"`
|
2024-03-17 10:21:41 +00:00
|
|
|
|
|
|
|
|
WebhookSecret string `json:"-"`
|
2024-03-17 05:59:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (g GithubEntity) LabelScope() string {
|
2024-03-17 11:07:44 +00:00
|
|
|
switch g.EntityType {
|
|
|
|
|
case GithubEntityTypeRepository:
|
|
|
|
|
return MetricsLabelRepositoryScope
|
|
|
|
|
case GithubEntityTypeOrganization:
|
|
|
|
|
return MetricsLabelOrganizationScope
|
|
|
|
|
case GithubEntityTypeEnterprise:
|
|
|
|
|
return MetricsLabelEnterpriseScope
|
|
|
|
|
}
|
|
|
|
|
return ""
|
2024-03-17 05:59:47 +00:00
|
|
|
}
|
2024-03-17 10:21:41 +00:00
|
|
|
|
|
|
|
|
func (g GithubEntity) String() string {
|
|
|
|
|
switch g.EntityType {
|
|
|
|
|
case GithubEntityTypeRepository:
|
|
|
|
|
return fmt.Sprintf("%s/%s", g.Owner, g.Name)
|
2024-03-17 18:08:46 +00:00
|
|
|
case GithubEntityTypeOrganization, GithubEntityTypeEnterprise:
|
2024-03-17 10:21:41 +00:00
|
|
|
return g.Owner
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
2024-04-15 08:32:19 +00:00
|
|
|
|
2024-04-18 16:50:46 +00:00
|
|
|
// used by swagger client generated code
|
|
|
|
|
type GithubEndpoints []GithubEndpoint
|
|
|
|
|
|
2024-04-15 08:32:19 +00:00
|
|
|
type GithubEndpoint struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Description string `json:"description"`
|
|
|
|
|
APIBaseURL string `json:"api_base_url"`
|
|
|
|
|
UploadBaseURL string `json:"upload_base_url"`
|
|
|
|
|
BaseURL string `json:"base_url"`
|
2024-04-18 16:50:46 +00:00
|
|
|
CACertBundle []byte `json:"ca_cert_bundle,omitempty"`
|
2024-04-15 08:32:19 +00:00
|
|
|
|
2024-04-18 16:50:46 +00:00
|
|
|
Credentials []GithubCredentials `json:"credentials,omitempty"`
|
2024-04-15 08:32:19 +00:00
|
|
|
}
|