garm/database/sql/models.go
Gabriel Adrian Samfira cc6e985629 Fix: Scope entities to endpoint
This change scopes all github entities to a github endpoint, allowing
users to have the same repo/org/enterprise created for each endpoint.

This way, if your username is the same on github.com and on your GHES
server, and you have the same repository name or org in both places,
GARM can now handle that situation.

This change also fixes a leaky watcher in the pool manager.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2024-07-29 17:35:57 +00:00

312 lines
10 KiB
Go

// 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 sql
import (
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
"gorm.io/datatypes"
"gorm.io/gorm"
commonParams "github.com/cloudbase/garm-provider-common/params"
"github.com/cloudbase/garm/params"
)
type Base struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (b *Base) BeforeCreate(_ *gorm.DB) error {
emptyID := uuid.UUID{}
if b.ID != emptyID {
return nil
}
newID, err := uuid.NewRandom()
if err != nil {
return errors.Wrap(err, "generating id")
}
b.ID = newID
return nil
}
type Tag struct {
Base
Name string `gorm:"type:varchar(64);uniqueIndex"`
Pools []*Pool `gorm:"many2many:pool_tags;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"`
}
type Pool struct {
Base
ProviderName string `gorm:"index:idx_pool_type"`
RunnerPrefix string
MaxRunners uint
MinIdleRunners uint
RunnerBootstrapTimeout uint
Image string `gorm:"index:idx_pool_type"`
Flavor string `gorm:"index:idx_pool_type"`
OSType commonParams.OSType
OSArch commonParams.OSArch
Tags []*Tag `gorm:"many2many:pool_tags;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"`
Enabled bool
// ExtraSpecs is an opaque 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.
ExtraSpecs datatypes.JSON
GitHubRunnerGroup string
RepoID *uuid.UUID `gorm:"index"`
Repository Repository `gorm:"foreignKey:RepoID;"`
OrgID *uuid.UUID `gorm:"index"`
Organization Organization `gorm:"foreignKey:OrgID"`
EnterpriseID *uuid.UUID `gorm:"index"`
Enterprise Enterprise `gorm:"foreignKey:EnterpriseID"`
Instances []Instance `gorm:"foreignKey:PoolID"`
Priority uint `gorm:"index:idx_pool_priority"`
}
type Repository struct {
Base
CredentialsName string
CredentialsID *uint `gorm:"index"`
Credentials GithubCredentials `gorm:"foreignKey:CredentialsID;constraint:OnDelete:SET NULL"`
Owner string `gorm:"index:idx_owner_nocase,unique,collate:nocase"`
Name string `gorm:"index:idx_owner_nocase,unique,collate:nocase"`
WebhookSecret []byte
Pools []Pool `gorm:"foreignKey:RepoID"`
Jobs []WorkflowJob `gorm:"foreignKey:RepoID;constraint:OnDelete:SET NULL"`
PoolBalancerType params.PoolBalancerType `gorm:"type:varchar(64)"`
EndpointName *string `gorm:"index:idx_owner_nocase,unique,collate:nocase"`
Endpoint GithubEndpoint `gorm:"foreignKey:EndpointName;constraint:OnDelete:SET NULL"`
}
type Organization struct {
Base
CredentialsName string
CredentialsID *uint `gorm:"index"`
Credentials GithubCredentials `gorm:"foreignKey:CredentialsID;constraint:OnDelete:SET NULL"`
Name string `gorm:"index:idx_org_name_nocase,collate:nocase"`
WebhookSecret []byte
Pools []Pool `gorm:"foreignKey:OrgID"`
Jobs []WorkflowJob `gorm:"foreignKey:OrgID;constraint:OnDelete:SET NULL"`
PoolBalancerType params.PoolBalancerType `gorm:"type:varchar(64)"`
EndpointName *string `gorm:"index:idx_org_name_nocase,collate:nocase"`
Endpoint GithubEndpoint `gorm:"foreignKey:EndpointName;constraint:OnDelete:SET NULL"`
}
type Enterprise struct {
Base
CredentialsName string
CredentialsID *uint `gorm:"index"`
Credentials GithubCredentials `gorm:"foreignKey:CredentialsID;constraint:OnDelete:SET NULL"`
Name string `gorm:"index:idx_ent_name_nocase,collate:nocase"`
WebhookSecret []byte
Pools []Pool `gorm:"foreignKey:EnterpriseID"`
Jobs []WorkflowJob `gorm:"foreignKey:EnterpriseID;constraint:OnDelete:SET NULL"`
PoolBalancerType params.PoolBalancerType `gorm:"type:varchar(64)"`
EndpointName *string `gorm:"index:idx_ent_name_nocase,collate:nocase"`
Endpoint GithubEndpoint `gorm:"foreignKey:EndpointName;constraint:OnDelete:SET NULL"`
}
type Address struct {
Base
Address string
Type string
InstanceID uuid.UUID
Instance Instance `gorm:"foreignKey:InstanceID"`
}
type InstanceStatusUpdate struct {
Base
EventType params.EventType `gorm:"index:eventType"`
EventLevel params.EventLevel
Message string `gorm:"type:text"`
InstanceID uuid.UUID `gorm:"index:idx_instance_status_updates_instance_id"`
Instance Instance `gorm:"foreignKey:InstanceID"`
}
type Instance struct {
Base
ProviderID *string `gorm:"uniqueIndex"`
Name string `gorm:"uniqueIndex"`
AgentID int64
OSType commonParams.OSType
OSArch commonParams.OSArch
OSName string
OSVersion string
Addresses []Address `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"`
Status commonParams.InstanceStatus
RunnerStatus params.RunnerStatus
CallbackURL string
MetadataURL string
ProviderFault []byte `gorm:"type:longblob"`
CreateAttempt int
TokenFetched bool
JitConfiguration []byte `gorm:"type:longblob"`
GitHubRunnerGroup string
AditionalLabels datatypes.JSON
PoolID uuid.UUID
Pool Pool `gorm:"foreignKey:PoolID"`
StatusMessages []InstanceStatusUpdate `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"`
Job *WorkflowJob `gorm:"foreignKey:InstanceID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"`
}
type User struct {
Base
Username string `gorm:"uniqueIndex;varchar(64)"`
FullName string `gorm:"type:varchar(254)"`
Email string `gorm:"type:varchar(254);unique;index:idx_email"`
Password string `gorm:"type:varchar(60)"`
Generation uint
IsAdmin bool
Enabled bool
}
type ControllerInfo struct {
Base
ControllerID uuid.UUID
CallbackURL string
MetadataURL string
WebhookBaseURL string
// MinimumJobAgeBackoff is the minimum time that a job must be in the queue
// before GARM will attempt to allocate a runner to service it. This backoff
// is useful if you have idle runners in various pools that could potentially
// pick up the job. GARM would allow this amount of time for runners to react
// before spinning up a new one and potentially having to scale down later.
MinimumJobAgeBackoff uint
}
type WorkflowJob struct {
// ID is the ID of the job.
ID int64 `gorm:"index"`
// RunID is the ID of the workflow run. A run may have multiple jobs.
RunID int64
// Action is the specific activity that triggered the event.
Action string `gorm:"type:varchar(254);index"`
// Conclusion is the outcome of the job.
// Possible values: "success", "failure", "neutral", "cancelled", "skipped",
// "timed_out", "action_required"
Conclusion string
// Status is the phase of the lifecycle that the job is currently in.
// "queued", "in_progress" and "completed".
Status string
// Name is the name if the job that was triggered.
Name string
StartedAt time.Time
CompletedAt time.Time
GithubRunnerID int64
InstanceID *uuid.UUID `gorm:"index:idx_instance_job"`
Instance Instance `gorm:"foreignKey:InstanceID"`
RunnerGroupID int64
RunnerGroupName string
// repository in which the job was triggered.
RepositoryName string
RepositoryOwner string
Labels datatypes.JSON
// 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.
RepoID *uuid.UUID `gorm:"index"`
Repository Repository `gorm:"foreignKey:RepoID"`
OrgID *uuid.UUID `gorm:"index"`
Organization Organization `gorm:"foreignKey:OrgID"`
EnterpriseID *uuid.UUID `gorm:"index"`
Enterprise Enterprise `gorm:"foreignKey:EnterpriseID"`
LockedBy uuid.UUID
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
type GithubEndpoint struct {
Name string `gorm:"type:varchar(64) collate nocase;primary_key;"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Description string `gorm:"type:text"`
APIBaseURL string `gorm:"type:text collate nocase"`
UploadBaseURL string `gorm:"type:text collate nocase"`
BaseURL string `gorm:"type:text collate nocase"`
CACertBundle []byte `gorm:"type:longblob"`
}
type GithubCredentials struct {
gorm.Model
Name string `gorm:"index:idx_github_credentials,unique;type:varchar(64) collate nocase"`
UserID *uuid.UUID `gorm:"index:idx_github_credentials,unique"`
User User `gorm:"foreignKey:UserID"`
Description string `gorm:"type:text"`
AuthType params.GithubAuthType `gorm:"index"`
Payload []byte `gorm:"type:longblob"`
Endpoint GithubEndpoint `gorm:"foreignKey:EndpointName"`
EndpointName *string `gorm:"index"`
Repositories []Repository `gorm:"foreignKey:CredentialsID"`
Organizations []Organization `gorm:"foreignKey:CredentialsID"`
Enterprises []Enterprise `gorm:"foreignKey:CredentialsID"`
}