From ef676488b70f2bc18067db9b0d14a64b6516e119 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Sat, 10 May 2025 20:08:51 +0000 Subject: [PATCH 01/21] Use cache for github client Signed-off-by: Gabriel Adrian Samfira --- cache/github_client.go | 47 +++++++++++ workers/entity/util.go | 18 ++++ workers/entity/worker.go | 109 ++++++++++++++++++++++++- workers/entity/worker_watcher.go | 16 ++++ workers/pools/controller.go | 3 + workers/scaleset/controller.go | 76 ++--------------- workers/scaleset/controller_watcher.go | 68 +-------------- workers/scaleset/interfaces.go | 2 +- workers/scaleset/scaleset.go | 76 ++++++++--------- workers/scaleset/scaleset_helper.go | 19 ++++- workers/scaleset/scaleset_listener.go | 20 ++++- workers/scaleset/util.go | 15 ---- 12 files changed, 277 insertions(+), 192 deletions(-) create mode 100644 cache/github_client.go create mode 100644 workers/pools/controller.go diff --git a/cache/github_client.go b/cache/github_client.go new file mode 100644 index 00000000..0126e45b --- /dev/null +++ b/cache/github_client.go @@ -0,0 +1,47 @@ +package cache + +import ( + "sync" + + "github.com/cloudbase/garm/runner/common" +) + +var ghClientCache *GithubClientCache + +type GithubClientCache struct { + mux sync.Mutex + + cache map[string]common.GithubClient +} + +func init() { + clientCache := &GithubClientCache{ + cache: make(map[string]common.GithubClient), + } + ghClientCache = clientCache +} + +func (g *GithubClientCache) SetClient(entityID string, client common.GithubClient) { + g.mux.Lock() + defer g.mux.Unlock() + + g.cache[entityID] = client +} + +func (g *GithubClientCache) GetClient(entityID string) (common.GithubClient, bool) { + g.mux.Lock() + defer g.mux.Unlock() + + if client, ok := g.cache[entityID]; ok { + return client, true + } + return nil, false +} + +func SetGithubClient(entityID string, client common.GithubClient) { + ghClientCache.SetClient(entityID, client) +} + +func GetGithubClient(entityID string) (common.GithubClient, bool) { + return ghClientCache.GetClient(entityID) +} diff --git a/workers/entity/util.go b/workers/entity/util.go index 4912beba..95c9b2cc 100644 --- a/workers/entity/util.go +++ b/workers/entity/util.go @@ -1,6 +1,8 @@ package entity import ( + "strings" + "golang.org/x/sync/errgroup" dbCommon "github.com/cloudbase/garm/database/common" @@ -8,6 +10,13 @@ import ( "github.com/cloudbase/garm/params" ) +const ( + // These are duplicated until we decide if we move the pool manager to the new + // worker flow. + poolIDLabelprefix = "runner-pool-id:" + controllerLabelPrefix = "runner-controller-id:" +) + func composeControllerWatcherFilters() dbCommon.PayloadFilterFunc { return watcher.WithAll( watcher.WithAny( @@ -56,3 +65,12 @@ func (c *Controller) waitForErrorGroupOrContextCancelled(g *errgroup.Group) erro return nil } } + +func poolIDFromLabels(runner params.RunnerReference) string { + for _, lbl := range runner.Labels { + if strings.HasPrefix(lbl.Name, poolIDLabelprefix) { + return lbl.Name[len(poolIDLabelprefix):] + } + } + return "" +} diff --git a/workers/entity/worker.go b/workers/entity/worker.go index 8aebb747..fcfd2a60 100644 --- a/workers/entity/worker.go +++ b/workers/entity/worker.go @@ -5,13 +5,18 @@ import ( "fmt" "log/slog" "sync" + "time" + "github.com/cloudbase/garm/cache" dbCommon "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/database/watcher" "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner/common" garmUtil "github.com/cloudbase/garm/util" + "github.com/cloudbase/garm/util/github" + "github.com/cloudbase/garm/util/github/scalesets" "github.com/cloudbase/garm/workers/scaleset" + "golang.org/x/sync/errgroup" ) func NewWorker(ctx context.Context, store dbCommon.Store, entity params.GithubEntity, providers map[string]common.Provider) (*Worker, error) { @@ -36,6 +41,7 @@ type Worker struct { consumer dbCommon.Consumer store dbCommon.Store + ghCli common.GithubClient Entity params.GithubEntity providers map[string]common.Provider @@ -71,6 +77,13 @@ func (w *Worker) Start() (err error) { w.mux.Lock() defer w.mux.Unlock() + ghCli, err := github.Client(w.ctx, w.Entity) + if err != nil { + return fmt.Errorf("creating github client: %w", err) + } + w.ghCli = ghCli + cache.SetGithubClient(w.Entity.ID, ghCli) + scaleSetController, err := scaleset.NewController(w.ctx, w.store, w.Entity, w.providers) if err != nil { return fmt.Errorf("creating scale set controller: %w", err) @@ -100,16 +113,110 @@ func (w *Worker) Start() (err error) { w.quit = make(chan struct{}) go w.loop() + go w.consolidateRunnerLoop() return nil } +// consolidateRunnerState will list all runners on GitHub for this entity, sort by +// pool or scale set and pass those runners to the appropriate controller (pools or scale sets). +// The controller will then pass along to their respective workers the list of runners +// they should be responsible for. The workers will then cross check the current state +// from github with their local state and reconcile any differences. This cleans up +// any runners that have been removed out of band in either the provider or github. +func (w *Worker) consolidateRunnerState() error { + scaleSetCli, err := scalesets.NewClient(w.ghCli) + if err != nil { + return fmt.Errorf("creating scaleset client: %w", err) + } + // Client is scoped to the current entity. Only runners in a repo/org/enterprise + // will be listed. + runners, err := scaleSetCli.ListAllRunners(w.ctx) + if err != nil { + return fmt.Errorf("listing runners: %w", err) + } + + byPoolID := make(map[string][]params.RunnerReference) + byScaleSetID := make(map[int][]params.RunnerReference) + for _, runner := range runners.RunnerReferences { + if runner.RunnerScaleSetID != 0 { + byScaleSetID[runner.RunnerScaleSetID] = append(byScaleSetID[runner.RunnerScaleSetID], runner) + } else { + poolID := poolIDFromLabels(runner) + if poolID == "" { + continue + } + byPoolID[poolID] = append(byPoolID[poolID], runner) + } + } + + g, ctx := errgroup.WithContext(w.ctx) + g.Go(func() error { + slog.DebugContext(ctx, "consolidating scale set runners", "entity", w.Entity.String(), "runners", runners) + if err := w.scaleSetController.ConsolidateRunnerState(byScaleSetID); err != nil { + return fmt.Errorf("consolidating runners for scale set: %w", err) + } + return nil + }) + + if err := w.waitForErrorGroupOrContextCancelled(g); err != nil { + return fmt.Errorf("waiting for error group: %w", err) + } + return nil +} + +func (w *Worker) waitForErrorGroupOrContextCancelled(g *errgroup.Group) error { + if g == nil { + return nil + } + + done := make(chan error, 1) + go func() { + waitErr := g.Wait() + done <- waitErr + }() + + select { + case err := <-done: + return err + case <-w.ctx.Done(): + return w.ctx.Err() + case <-w.quit: + return nil + } +} + +func (w *Worker) consolidateRunnerLoop() { + ticker := time.NewTicker(common.PoolReapTimeoutInterval) + defer ticker.Stop() + + for { + select { + case _, ok := <-ticker.C: + if !ok { + slog.InfoContext(w.ctx, "consolidate ticker closed") + return + } + if err := w.consolidateRunnerState(); err != nil { + if err := w.store.AddEntityEvent(w.ctx, w.Entity, params.StatusEvent, params.EventError, fmt.Sprintf("failed to consolidate runner state: %q", err.Error()), 30); err != nil { + slog.With(slog.Any("error", err)).Error("failed to add entity event") + } + slog.With(slog.Any("error", err)).Error("failed to consolidate runner state") + } + case <-w.ctx.Done(): + return + case <-w.quit: + return + } + } +} + func (w *Worker) loop() { defer w.Stop() for { select { case payload := <-w.consumer.Watch(): slog.InfoContext(w.ctx, "received payload") - go w.handleWorkerWatcherEvent(payload) + w.handleWorkerWatcherEvent(payload) case <-w.ctx.Done(): return case <-w.quit: diff --git a/workers/entity/worker_watcher.go b/workers/entity/worker_watcher.go index 4ce83ddf..be0b6b3c 100644 --- a/workers/entity/worker_watcher.go +++ b/workers/entity/worker_watcher.go @@ -3,8 +3,10 @@ package entity import ( "log/slog" + "github.com/cloudbase/garm/cache" dbCommon "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/params" + "github.com/cloudbase/garm/util/github" ) func (w *Worker) handleWorkerWatcherEvent(event dbCommon.ChangePayload) { @@ -46,6 +48,13 @@ func (w *Worker) handleEntityEventPayload(event dbCommon.ChangePayload) { // credentials were swapped on the entity. We need to recompose the watcher // filters. w.consumer.SetFilters(composeWorkerWatcherFilters(entity)) + ghCli, err := github.Client(w.ctx, entity) + if err != nil { + slog.ErrorContext(w.ctx, "creating github client", "entity_id", entity.ID, "error", err) + return + } + w.ghCli = ghCli + cache.SetGithubClient(entity.ID, ghCli) } w.Entity = entity default: @@ -72,6 +81,13 @@ func (w *Worker) handleEntityCredentialsEventPayload(event dbCommon.ChangePayloa return } w.Entity.Credentials = credentials + ghCli, err := github.Client(w.ctx, w.Entity) + if err != nil { + slog.ErrorContext(w.ctx, "creating github client", "entity_id", w.Entity.ID, "error", err) + return + } + w.ghCli = ghCli + cache.SetGithubClient(w.Entity.ID, ghCli) default: slog.ErrorContext(w.ctx, "invalid operation type", "operation_type", event.Operation) } diff --git a/workers/pools/controller.go b/workers/pools/controller.go new file mode 100644 index 00000000..458766a9 --- /dev/null +++ b/workers/pools/controller.go @@ -0,0 +1,3 @@ +package pools + + diff --git a/workers/scaleset/controller.go b/workers/scaleset/controller.go index 3b4287c2..6e3170a0 100644 --- a/workers/scaleset/controller.go +++ b/workers/scaleset/controller.go @@ -5,7 +5,6 @@ import ( "fmt" "log/slog" "sync" - "time" "golang.org/x/sync/errgroup" @@ -14,15 +13,6 @@ import ( "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner/common" garmUtil "github.com/cloudbase/garm/util" - "github.com/cloudbase/garm/util/github" - "github.com/cloudbase/garm/util/github/scalesets" -) - -const ( - // These are duplicated until we decide if we move the pool manager to the new - // worker flow. - poolIDLabelprefix = "runner-pool-id:" - controllerLabelPrefix = "runner-controller-id:" ) func NewController(ctx context.Context, store dbCommon.Store, entity params.GithubEntity, providers map[string]common.Provider) (*Controller, error) { @@ -73,14 +63,12 @@ type Controller struct { store dbCommon.Store providers map[string]common.Provider - ghCli common.GithubClient - mux sync.Mutex running bool quit chan struct{} } -func (c *Controller) loadAllScaleSets(cli common.GithubClient) error { +func (c *Controller) loadAllScaleSets() error { scaleSets, err := c.store.ListEntityScaleSets(c.ctx, c.Entity) if err != nil { return fmt.Errorf("listing scale sets: %w", err) @@ -88,7 +76,7 @@ func (c *Controller) loadAllScaleSets(cli common.GithubClient) error { for _, sSet := range scaleSets { slog.DebugContext(c.ctx, "loading scale set", "scale_set", sSet.ID) - if err := c.handleScaleSetCreateOperation(sSet, cli); err != nil { + if err := c.handleScaleSetCreateOperation(sSet); err != nil { slog.With(slog.Any("error", err)).ErrorContext(c.ctx, "failed to handle scale set create operation") continue } @@ -105,13 +93,8 @@ func (c *Controller) Start() (err error) { } c.mux.Unlock() - ghCli, err := github.Client(c.ctx, c.Entity) - if err != nil { - return fmt.Errorf("creating github client: %w", err) - } - slog.DebugContext(c.ctx, "loaging scale sets", "entity", c.Entity.String()) - if err := c.loadAllScaleSets(ghCli); err != nil { + if err := c.loadAllScaleSets(); err != nil { return fmt.Errorf("loading all scale sets: %w", err) } @@ -124,7 +107,6 @@ func (c *Controller) Start() (err error) { } c.mux.Lock() - c.ghCli = ghCli c.consumer = consumer c.running = true c.quit = make(chan struct{}) @@ -159,39 +141,11 @@ func (c *Controller) Stop() error { return nil } -// consolidateRunnerState will list all runners on GitHub for this entity, sort by -// pool or scale set and pass those runners to the appropriate worker. The worker will -// then have the responsibility to cross check the runners from github with what it -// knows should be true from the database. Any inconsistency needs to be handled. -// If we have an offline runner in github but no database entry for it, we remove the -// runner from github. If we have a runner that is active in the provider but does not -// exist in github, we remove it from the provider and the database. -func (c *Controller) consolidateRunnerState() error { - scaleSetCli, err := scalesets.NewClient(c.ghCli) - if err != nil { - return fmt.Errorf("creating scaleset client: %w", err) - } - // Client is scoped to the current entity. Only runners in a repo/org/enterprise - // will be listed. - runners, err := scaleSetCli.ListAllRunners(c.ctx) - if err != nil { - return fmt.Errorf("listing runners: %w", err) - } - - byPoolID := make(map[string][]params.RunnerReference) - byScaleSetID := make(map[int][]params.RunnerReference) - for _, runner := range runners.RunnerReferences { - if runner.RunnerScaleSetID != 0 { - byScaleSetID[runner.RunnerScaleSetID] = append(byScaleSetID[runner.RunnerScaleSetID], runner) - } else { - poolID := poolIDFromLabels(runner) - if poolID == "" { - continue - } - byPoolID[poolID] = append(byPoolID[poolID], runner) - } - } - +// ConsolidateRunnerState will send a list of existing github runners to each scale set worker. +// The scale set worker will then need to cross check the existing runners in Github with the sate +// in the database. Any inconsistencies will b reconciliated. This cleans up any manually removed +// runners in either github or the providers. +func (c *Controller) ConsolidateRunnerState(byScaleSetID map[int][]params.RunnerReference) error { g, ctx := errgroup.WithContext(c.ctx) for _, scaleSet := range c.ScaleSets { runners := byScaleSetID[scaleSet.scaleSet.ScaleSetID] @@ -233,9 +187,6 @@ func (c *Controller) waitForErrorGroupOrContextCancelled(g *errgroup.Group) erro func (c *Controller) loop() { defer c.Stop() - consolidateTicker := time.NewTicker(common.PoolReapTimeoutInterval) - defer consolidateTicker.Stop() - for { select { case payload, ok := <-c.consumer.Watch(): @@ -247,17 +198,6 @@ func (c *Controller) loop() { c.handleWatcherEvent(payload) case <-c.ctx.Done(): return - case _, ok := <-consolidateTicker.C: - if !ok { - slog.InfoContext(c.ctx, "consolidate ticker closed") - return - } - if err := c.consolidateRunnerState(); err != nil { - if err := c.store.AddEntityEvent(c.ctx, c.Entity, params.StatusEvent, params.EventError, fmt.Sprintf("failed to consolidate runner state: %q", err.Error()), 30); err != nil { - slog.With(slog.Any("error", err)).Error("failed to add entity event") - } - slog.With(slog.Any("error", err)).Error("failed to consolidate runner state") - } case <-c.quit: return } diff --git a/workers/scaleset/controller_watcher.go b/workers/scaleset/controller_watcher.go index 6702e0f0..ec4771fc 100644 --- a/workers/scaleset/controller_watcher.go +++ b/workers/scaleset/controller_watcher.go @@ -6,8 +6,6 @@ import ( dbCommon "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/params" - "github.com/cloudbase/garm/runner/common" - "github.com/cloudbase/garm/util/github" ) func (c *Controller) handleWatcherEvent(event dbCommon.ChangePayload) { @@ -19,9 +17,6 @@ func (c *Controller) handleWatcherEvent(event dbCommon.ChangePayload) { case entityType: slog.DebugContext(c.ctx, "got entity payload event") c.handleEntityEvent(event) - case dbCommon.GithubCredentialsEntityType: - slog.DebugContext(c.ctx, "got github credentials payload event") - c.handleCredentialsEvent(event) default: slog.ErrorContext(c.ctx, "invalid entity type", "entity_type", event.EntityType) return @@ -38,7 +33,7 @@ func (c *Controller) handleScaleSet(event dbCommon.ChangePayload) { switch event.Operation { case dbCommon.CreateOperation: slog.DebugContext(c.ctx, "got create operation for scale set", "scale_set_id", scaleSet.ID, "scale_set_name", scaleSet.Name) - if err := c.handleScaleSetCreateOperation(scaleSet, c.ghCli); err != nil { + if err := c.handleScaleSetCreateOperation(scaleSet); err != nil { slog.With(slog.Any("error", err)).ErrorContext(c.ctx, "failed to handle scale set create operation") } case dbCommon.UpdateOperation: @@ -57,7 +52,7 @@ func (c *Controller) handleScaleSet(event dbCommon.ChangePayload) { } } -func (c *Controller) handleScaleSetCreateOperation(sSet params.ScaleSet, ghCli common.GithubClient) error { +func (c *Controller) handleScaleSetCreateOperation(sSet params.ScaleSet) error { c.mux.Lock() defer c.mux.Unlock() @@ -74,7 +69,7 @@ func (c *Controller) handleScaleSetCreateOperation(sSet params.ScaleSet, ghCli c return fmt.Errorf("provider %s not found for scale set %s", sSet.ProviderName, sSet.Name) } - worker, err := NewWorker(c.ctx, c.store, sSet, provider, ghCli) + worker, err := NewWorker(c.ctx, c.store, sSet, provider) if err != nil { return fmt.Errorf("creating scale set worker: %w", err) } @@ -120,7 +115,7 @@ func (c *Controller) handleScaleSetUpdateOperation(sSet params.ScaleSet) error { // Some error may have occurred when the scale set was first created, so we // attempt to create it after the user updated the scale set, hopefully // fixing the reason for the failure. - return c.handleScaleSetCreateOperation(sSet, c.ghCli) + return c.handleScaleSetCreateOperation(sSet) } set.scaleSet = sSet c.ScaleSets[sSet.ID] = set @@ -128,35 +123,6 @@ func (c *Controller) handleScaleSetUpdateOperation(sSet params.ScaleSet) error { return nil } -func (c *Controller) handleCredentialsEvent(event dbCommon.ChangePayload) { - credentials, ok := event.Payload.(params.GithubCredentials) - if !ok { - slog.ErrorContext(c.ctx, "invalid credentials payload for entity type", "entity_type", event.EntityType, "payload", event) - return - } - - switch event.Operation { - case dbCommon.UpdateOperation: - slog.DebugContext(c.ctx, "got update operation") - c.mux.Lock() - defer c.mux.Unlock() - - if c.Entity.Credentials.ID != credentials.ID { - // stale update event. - return - } - c.Entity.Credentials = credentials - - if err := c.updateAndBroadcastCredentials(c.Entity); err != nil { - slog.With(slog.Any("error", err)).ErrorContext(c.ctx, "failed to update credentials") - return - } - default: - slog.ErrorContext(c.ctx, "invalid operation type", "operation_type", event.Operation) - return - } -} - func (c *Controller) handleEntityEvent(event dbCommon.ChangePayload) { var entityGetter params.EntityGetter var ok bool @@ -184,35 +150,9 @@ func (c *Controller) handleEntityEvent(event dbCommon.ChangePayload) { slog.DebugContext(c.ctx, "got update operation") c.mux.Lock() defer c.mux.Unlock() - - if c.Entity.Credentials.ID != entity.Credentials.ID { - // credentials were swapped on the entity. We need to recompose the watcher - // filters. - c.consumer.SetFilters(composeControllerWatcherFilters(entity)) - if err := c.updateAndBroadcastCredentials(c.Entity); err != nil { - slog.With(slog.Any("error", err)).ErrorContext(c.ctx, "failed to update credentials") - } - } c.Entity = entity default: slog.ErrorContext(c.ctx, "invalid operation type", "operation_type", event.Operation) return } } - -func (c *Controller) updateAndBroadcastCredentials(entity params.GithubEntity) error { - ghCli, err := github.Client(c.ctx, entity) - if err != nil { - return fmt.Errorf("creating github client: %w", err) - } - - c.ghCli = ghCli - - for _, scaleSet := range c.ScaleSets { - if err := scaleSet.worker.SetGithubClient(ghCli); err != nil { - slog.ErrorContext(c.ctx, "setting github client on worker", "error", err) - continue - } - } - return nil -} diff --git a/workers/scaleset/interfaces.go b/workers/scaleset/interfaces.go index ee089c25..ca529ce1 100644 --- a/workers/scaleset/interfaces.go +++ b/workers/scaleset/interfaces.go @@ -6,8 +6,8 @@ import ( ) type scaleSetHelper interface { - ScaleSetCLI() *scalesets.ScaleSetClient GetScaleSet() params.ScaleSet + GetScaleSetClient() (*scalesets.ScaleSetClient, error) SetLastMessageID(id int64) error SetDesiredRunnerCount(count int) error Owner() string diff --git a/workers/scaleset/scaleset.go b/workers/scaleset/scaleset.go index 73d08c98..097a8680 100644 --- a/workers/scaleset/scaleset.go +++ b/workers/scaleset/scaleset.go @@ -17,19 +17,14 @@ import ( "github.com/cloudbase/garm/locking" "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner/common" - "github.com/cloudbase/garm/util/github/scalesets" ) -func NewWorker(ctx context.Context, store dbCommon.Store, scaleSet params.ScaleSet, provider common.Provider, ghCli common.GithubClient) (*Worker, error) { +func NewWorker(ctx context.Context, store dbCommon.Store, scaleSet params.ScaleSet, provider common.Provider) (*Worker, error) { consumerID := fmt.Sprintf("scaleset-worker-%s-%d", scaleSet.Name, scaleSet.ID) controllerInfo, err := store.ControllerInfo() if err != nil { return nil, fmt.Errorf("getting controller info: %w", err) } - scaleSetCli, err := scalesets.NewClient(ghCli) - if err != nil { - return nil, fmt.Errorf("creating scale set client: %w", err) - } return &Worker{ ctx: ctx, controllerInfo: controllerInfo, @@ -37,8 +32,6 @@ func NewWorker(ctx context.Context, store dbCommon.Store, scaleSet params.ScaleS store: store, provider: provider, scaleSet: scaleSet, - ghCli: ghCli, - scaleSetCli: scaleSetCli, runners: make(map[string]params.Instance), }, nil } @@ -53,9 +46,7 @@ type Worker struct { scaleSet params.ScaleSet runners map[string]params.Instance - ghCli common.GithubClient - scaleSetCli *scalesets.ScaleSetClient - consumer dbCommon.Consumer + consumer dbCommon.Consumer listener *scaleSetListener @@ -110,7 +101,12 @@ func (w *Worker) Start() (err error) { instanceState := commonParams.InstancePendingDelete locking.Lock(instance.Name, w.consumerID) if instance.AgentID != 0 { - if err := w.scaleSetCli.RemoveRunner(w.ctx, instance.AgentID); err != nil { + scaleSetCli, err := w.GetScaleSetClient() + if err != nil { + slog.ErrorContext(w.ctx, "error getting scale set client", "error", err) + return fmt.Errorf("getting scale set client: %w", err) + } + if err := scaleSetCli.RemoveRunner(w.ctx, instance.AgentID); err != nil { // scale sets use JIT runners. This means that we create the runner in github // before we create the actual instance that will use the credentials. We need // to remove the runner from github if it exists. @@ -128,7 +124,7 @@ func (w *Worker) Start() (err error) { } // The runner may have come up, registered and is currently running a // job, in which case, github will not allow us to remove it. - runnerInstance, err := w.scaleSetCli.GetRunner(w.ctx, instance.AgentID) + runnerInstance, err := scaleSetCli.GetRunner(w.ctx, instance.AgentID) if err != nil { if !errors.Is(err, runnerErrors.ErrNotFound) { // We could not get info about the runner and it wasn't not found @@ -254,7 +250,11 @@ func (w *Worker) setRunnerDBStatus(runner string, status commonParams.InstanceSt } func (w *Worker) removeRunnerFromGithubAndSetPendingDelete(runnerName string, agentID int64) error { - if err := w.scaleSetCli.RemoveRunner(w.ctx, agentID); err != nil { + scaleSetCli, err := w.GetScaleSetClient() + if err != nil { + return fmt.Errorf("getting scale set client: %w", err) + } + if err := scaleSetCli.RemoveRunner(w.ctx, agentID); err != nil { if !errors.Is(err, runnerErrors.ErrNotFound) { return fmt.Errorf("removing runner %s: %w", runnerName, err) } @@ -321,6 +321,10 @@ func (w *Worker) consolidateRunnerState(runners []params.RunnerReference) error ghRunnersByName[runner.Name] = runner } + scaleSetCli, err := w.GetScaleSetClient() + if err != nil { + return fmt.Errorf("getting scale set client: %w", err) + } dbRunnersByName := w.runnerByName() // Cross check what exists in github with what we have in the database. for name, runner := range ghRunnersByName { @@ -329,7 +333,7 @@ func (w *Worker) consolidateRunnerState(runners []params.RunnerReference) error // runner appears to be active. Is it not managed by GARM? if status != params.RunnerIdle && status != params.RunnerActive { slog.InfoContext(w.ctx, "runner does not exist in GARM; removing from github", "runner_name", name) - if err := w.scaleSetCli.RemoveRunner(w.ctx, runner.ID); err != nil { + if err := scaleSetCli.RemoveRunner(w.ctx, runner.ID); err != nil { if errors.Is(err, runnerErrors.ErrNotFound) { continue } @@ -466,23 +470,6 @@ func (w *Worker) consolidateRunnerState(runners []params.RunnerReference) error return nil } -func (w *Worker) SetGithubClient(client common.GithubClient) error { - w.mux.Lock() - defer w.mux.Unlock() - - if err := w.listener.Stop(); err != nil { - slog.ErrorContext(w.ctx, "error stopping listener", "error", err) - } - - w.ghCli = client - scaleSetCli, err := scalesets.NewClient(client) - if err != nil { - return fmt.Errorf("error creating scale set client: %w", err) - } - w.scaleSetCli = scaleSetCli - return nil -} - func (w *Worker) pseudoPoolID() (string, error) { // This is temporary. We need to extend providers to know about scale sets. entity, err := w.scaleSet.GetEntity() @@ -563,8 +550,13 @@ func (w *Worker) handleInstanceEntityEvent(event dbCommon.ChangePayload) { w.mux.Unlock() return } + scaleSetCli, err := w.GetScaleSetClient() + if err != nil { + slog.ErrorContext(w.ctx, "error getting scale set client", "error", err) + return + } if oldInstance.RunnerStatus != instance.RunnerStatus && instance.RunnerStatus == params.RunnerIdle { - serviceRuner, err := w.scaleSetCli.GetRunner(w.ctx, instance.AgentID) + serviceRuner, err := scaleSetCli.GetRunner(w.ctx, instance.AgentID) if err != nil { slog.ErrorContext(w.ctx, "error getting runner details", "error", err) w.mux.Unlock() @@ -725,9 +717,14 @@ func (w *Worker) handleScaleUp(target, current uint) { return } + scaleSetCli, err := w.GetScaleSetClient() + if err != nil { + slog.ErrorContext(w.ctx, "error getting scale set client", "error", err) + return + } for i := current; i < target; i++ { newRunnerName := fmt.Sprintf("%s-%s", w.scaleSet.GetRunnerPrefix(), util.NewID()) - jitConfig, err := w.scaleSetCli.GenerateJitRunnerConfig(w.ctx, newRunnerName, w.scaleSet.ScaleSetID) + jitConfig, err := scaleSetCli.GenerateJitRunnerConfig(w.ctx, newRunnerName, w.scaleSet.ScaleSetID) if err != nil { slog.ErrorContext(w.ctx, "error generating jit config", "error", err) continue @@ -755,14 +752,14 @@ func (w *Worker) handleScaleUp(target, current uint) { dbInstance, err := w.store.CreateScaleSetInstance(w.ctx, w.scaleSet.ID, runnerParams) if err != nil { slog.ErrorContext(w.ctx, "error creating instance", "error", err) - if err := w.scaleSetCli.RemoveRunner(w.ctx, jitConfig.Runner.ID); err != nil { + if err := scaleSetCli.RemoveRunner(w.ctx, jitConfig.Runner.ID); err != nil { slog.ErrorContext(w.ctx, "error deleting runner", "error", err) } continue } w.runners[dbInstance.ID] = dbInstance - _, err = w.scaleSetCli.GetRunner(w.ctx, jitConfig.Runner.ID) + _, err = scaleSetCli.GetRunner(w.ctx, jitConfig.Runner.ID) if err != nil { slog.ErrorContext(w.ctx, "error getting runner details", "error", err) continue @@ -854,8 +851,13 @@ func (w *Worker) handleScaleDown(target, current uint) { continue } + scaleSetCli, err := w.GetScaleSetClient() + if err != nil { + slog.ErrorContext(w.ctx, "error getting scale set client", "error", err) + return + } slog.DebugContext(w.ctx, "removing runner", "runner_name", runner.Name) - if err := w.scaleSetCli.RemoveRunner(w.ctx, runner.AgentID); err != nil { + if err := scaleSetCli.RemoveRunner(w.ctx, runner.AgentID); err != nil { if !errors.Is(err, runnerErrors.ErrNotFound) { slog.ErrorContext(w.ctx, "error removing runner", "runner_name", runner.Name, "error", err) locking.Unlock(runner.Name, false) diff --git a/workers/scaleset/scaleset_helper.go b/workers/scaleset/scaleset_helper.go index c3302f75..26f845ff 100644 --- a/workers/scaleset/scaleset_helper.go +++ b/workers/scaleset/scaleset_helper.go @@ -7,13 +7,28 @@ import ( runnerErrors "github.com/cloudbase/garm-provider-common/errors" commonParams "github.com/cloudbase/garm-provider-common/params" + "github.com/cloudbase/garm/cache" "github.com/cloudbase/garm/locking" "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/util/github/scalesets" ) -func (w *Worker) ScaleSetCLI() *scalesets.ScaleSetClient { - return w.scaleSetCli +func (w *Worker) GetScaleSetClient() (*scalesets.ScaleSetClient, error) { + scaleSetEntity, err := w.scaleSet.GetEntity() + if err != nil { + return nil, fmt.Errorf("getting entity: %w", err) + } + + ghCli, ok := cache.GetGithubClient(scaleSetEntity.ID) + if !ok { + return nil, fmt.Errorf("getting github client: %w", err) + } + scaleSetClient, err := scalesets.NewClient(ghCli) + if err != nil { + return nil, fmt.Errorf("creating scale set client: %w", err) + } + + return scaleSetClient, nil } func (w *Worker) GetScaleSet() params.ScaleSet { diff --git a/workers/scaleset/scaleset_listener.go b/workers/scaleset/scaleset_listener.go index 7a521e46..7e0ec869 100644 --- a/workers/scaleset/scaleset_listener.go +++ b/workers/scaleset/scaleset_listener.go @@ -48,8 +48,12 @@ func (l *scaleSetListener) Start() error { l.listenerCtx, l.cancelFunc = context.WithCancel(context.Background()) scaleSet := l.scaleSetHelper.GetScaleSet() + scaleSetClient, err := l.scaleSetHelper.GetScaleSetClient() + if err != nil { + return fmt.Errorf("getting scale set client: %w", err) + } slog.DebugContext(l.ctx, "creating new message session", "scale_set", scaleSet.ScaleSetID) - session, err := l.scaleSetHelper.ScaleSetCLI().CreateMessageSession( + session, err := scaleSetClient.CreateMessageSession( l.listenerCtx, scaleSet.ScaleSetID, l.scaleSetHelper.Owner(), ) @@ -72,13 +76,16 @@ func (l *scaleSetListener) Stop() error { if !l.running { return nil } - + scaleSetClient, err := l.scaleSetHelper.GetScaleSetClient() + if err != nil { + return fmt.Errorf("getting scale set client: %w", err) + } if l.messageSession != nil { slog.DebugContext(l.ctx, "closing message session", "scale_set", l.scaleSetHelper.GetScaleSet().ScaleSetID) if err := l.messageSession.Close(); err != nil { slog.ErrorContext(l.ctx, "closing message session", "error", err) } - if err := l.scaleSetHelper.ScaleSetCLI().DeleteMessageSession(context.Background(), l.messageSession); err != nil { + if err := scaleSetClient.DeleteMessageSession(context.Background(), l.messageSession); err != nil { slog.ErrorContext(l.ctx, "error deleting message session", "error", err) } } @@ -145,12 +152,17 @@ func (l *scaleSetListener) handleSessionMessage(msg params.RunnerScaleSetMessage } } + scaleSetClient, err := l.scaleSetHelper.GetScaleSetClient() + if err != nil { + slog.ErrorContext(l.ctx, "getting scale set client", "error", err) + return + } if len(availableJobs) > 0 { jobIDs := make([]int64, len(availableJobs)) for idx, job := range availableJobs { jobIDs[idx] = job.RunnerRequestID } - idsAcquired, err := l.scaleSetHelper.ScaleSetCLI().AcquireJobs( + idsAcquired, err := scaleSetClient.AcquireJobs( l.listenerCtx, l.scaleSetHelper.GetScaleSet().ScaleSetID, l.messageSession.MessageQueueAccessToken(), jobIDs) if err != nil { diff --git a/workers/scaleset/util.go b/workers/scaleset/util.go index aa3156c7..02d33b69 100644 --- a/workers/scaleset/util.go +++ b/workers/scaleset/util.go @@ -1,8 +1,6 @@ package scaleset import ( - "strings" - dbCommon "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/database/watcher" "github.com/cloudbase/garm/params" @@ -22,18 +20,5 @@ func composeControllerWatcherFilters(entity params.GithubEntity) dbCommon.Payloa watcher.WithEntityFilter(entity), watcher.WithOperationTypeFilter(dbCommon.UpdateOperation), ), - watcher.WithAll( - watcher.WithGithubCredentialsFilter(entity.Credentials), - watcher.WithOperationTypeFilter(dbCommon.UpdateOperation), - ), ) } - -func poolIDFromLabels(runner params.RunnerReference) string { - for _, lbl := range runner.Labels { - if strings.HasPrefix(lbl.Name, poolIDLabelprefix) { - return lbl.Name[len(poolIDLabelprefix):] - } - } - return "" -} From 4890eb47325becea0bd2d8f3a88b09ac82f5cf0f Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Mon, 12 May 2025 17:32:37 +0000 Subject: [PATCH 02/21] Add EndpointType Signed-off-by: Gabriel Adrian Samfira --- apiserver/controllers/controllers.go | 4 ++++ database/sql/github.go | 1 + database/sql/models.go | 2 ++ database/sql/sql.go | 11 ++++++++++ params/params.go | 8 +++++++ params/requests.go | 33 ++++++++++++++++++---------- 6 files changed, 48 insertions(+), 11 deletions(-) diff --git a/apiserver/controllers/controllers.go b/apiserver/controllers/controllers.go index d8750a50..3e6413e0 100644 --- a/apiserver/controllers/controllers.go +++ b/apiserver/controllers/controllers.go @@ -103,6 +103,7 @@ func (a *APIController) handleWorkflowJobEvent(ctx context.Context, w http.Respo handleError(ctx, w, gErrors.NewBadRequestError("invalid post body: %s", err)) return } + slog.Debug("received workflow job event", "body", string(body)) signature := r.Header.Get("X-Hub-Signature-256") hookType := r.Header.Get("X-Github-Hook-Installation-Target-Type") @@ -154,6 +155,9 @@ func (a *APIController) WebhookHandler(w http.ResponseWriter, r *http.Request) { } headers := r.Header.Clone() + for k, v := range headers { + slog.Debug("header", "key", k, "value", v) + } event := runnerParams.Event(headers.Get("X-Github-Event")) switch event { diff --git a/database/sql/github.go b/database/sql/github.go index 22e357bd..2fb46d65 100644 --- a/database/sql/github.go +++ b/database/sql/github.go @@ -97,6 +97,7 @@ func (s *sqlDatabase) sqlToCommonGithubEndpoint(ep GithubEndpoint) (params.Githu UploadBaseURL: ep.UploadBaseURL, CACertBundle: ep.CACertBundle, CreatedAt: ep.CreatedAt, + EndpointType: ep.EndpointType, UpdatedAt: ep.UpdatedAt, }, nil } diff --git a/database/sql/models.go b/database/sql/models.go index d6fbb6e9..2a04db0c 100644 --- a/database/sql/models.go +++ b/database/sql/models.go @@ -381,6 +381,8 @@ type GithubEndpoint struct { UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` + EndpointType params.EndpointType + Description string `gorm:"type:text"` APIBaseURL string `gorm:"type:text collate nocase"` UploadBaseURL string `gorm:"type:text collate nocase"` diff --git a/database/sql/sql.go b/database/sql/sql.go index 76495732..86729ad6 100644 --- a/database/sql/sql.go +++ b/database/sql/sql.go @@ -409,6 +409,17 @@ func (s *sqlDatabase) migrateDB() error { } } + if s.conn.Migrator().HasTable(&GithubEndpoint{}) { + if !s.conn.Migrator().HasColumn(&GithubEndpoint{}, "endpoint_type") { + if err := s.conn.Migrator().AutoMigrate(&GithubEndpoint{}); err != nil { + return errors.Wrap(err, "migrating github endpoints") + } + if err := s.conn.Exec("update github_endpoints set endpoint_type = 'github' where endpoint_type is null").Error; err != nil { + return errors.Wrap(err, "updating github endpoints") + } + } + } + var needsCredentialMigration bool if !s.conn.Migrator().HasTable(&GithubCredentials{}) || !s.conn.Migrator().HasTable(&GithubEndpoint{}) { needsCredentialMigration = true diff --git a/params/params.go b/params/params.go index 7636102f..fdf3b836 100644 --- a/params/params.go +++ b/params/params.go @@ -44,6 +44,7 @@ type ( RunnerStatus string WebhookEndpointType string GithubAuthType string + EndpointType string PoolBalancerType string ScaleSetState string ScaleSetMessageType string @@ -76,6 +77,11 @@ const ( PoolBalancerTypeNone PoolBalancerType = "" ) +const ( + GithubEndpointType EndpointType = "github" + GiteaEndpointType EndpointType = "gitea" +) + const ( // LXDProvider represents the LXD provider. LXDProvider ProviderType = "lxd" @@ -1138,5 +1144,7 @@ type GithubEndpoint struct { CreatedAt time.Time `json:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"` + EndpointType EndpointType `json:"endpoint_type,omitempty"` + Credentials []GithubCredentials `json:"credentials,omitempty"` } diff --git a/params/requests.go b/params/requests.go index 1166418f..12d9b838 100644 --- a/params/requests.go +++ b/params/requests.go @@ -281,6 +281,7 @@ type CreateGithubEndpointParams struct { APIBaseURL string `json:"api_base_url,omitempty"` UploadBaseURL string `json:"upload_base_url,omitempty"` BaseURL string `json:"base_url,omitempty"` + EndpointType string `json:"endpoint_type,omitempty"` CACertBundle []byte `json:"ca_cert_bundle,omitempty"` } @@ -289,6 +290,14 @@ func (c CreateGithubEndpointParams) Validate() error { return runnerErrors.NewBadRequestError("missing api_base_url") } + if c.EndpointType != "" { + switch c.EndpointType { + case string(GithubEndpointType), string(GiteaEndpointType): + default: + return runnerErrors.NewBadRequestError("invalid endpoint_type: %s", c.EndpointType) + } + } + url, err := url.Parse(c.APIBaseURL) if err != nil || url.Scheme == "" || url.Host == "" { return runnerErrors.NewBadRequestError("invalid api_base_url") @@ -299,19 +308,21 @@ func (c CreateGithubEndpointParams) Validate() error { return runnerErrors.NewBadRequestError("invalid api_base_url") } - if c.UploadBaseURL == "" { - return runnerErrors.NewBadRequestError("missing upload_base_url") - } + if c.EndpointType == string(GithubEndpointType) { + if c.UploadBaseURL == "" { + return runnerErrors.NewBadRequestError("missing upload_base_url") + } - url, err = url.Parse(c.UploadBaseURL) - if err != nil || url.Scheme == "" || url.Host == "" { - return runnerErrors.NewBadRequestError("invalid upload_base_url") - } + url, err = url.Parse(c.UploadBaseURL) + if err != nil || url.Scheme == "" || url.Host == "" { + return runnerErrors.NewBadRequestError("invalid upload_base_url") + } - switch url.Scheme { - case httpsScheme, httpScheme: - default: - return runnerErrors.NewBadRequestError("invalid api_base_url") + switch url.Scheme { + case httpsScheme, httpScheme: + default: + return runnerErrors.NewBadRequestError("invalid api_base_url") + } } if c.BaseURL == "" { From 40e6581a759d5174d6f3054245fd28d5b0582026 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Mon, 12 May 2025 21:47:13 +0000 Subject: [PATCH 03/21] Rename GitHub specific types This change renames a lot of variables, types and functions to be more generic. The goal is to allow GARM to add more forges in the future. Signed-off-by: Gabriel Adrian Samfira --- apiserver/controllers/enterprises.go | 4 +- apiserver/controllers/organizations.go | 4 +- apiserver/controllers/repositories.go | 4 +- auth/instance_middleware.go | 4 +- auth/interfaces.go | 2 +- cache/cache_test.go | 69 ++-- cache/credentials_cache.go | 2 +- cache/entity_cache.go | 60 ++-- cache/tools_cache.go | 6 +- .../create_github_endpoint_responses.go | 4 +- .../get_github_endpoint_responses.go | 4 +- .../list_github_endpoints_responses.go | 4 +- .../update_github_endpoint_responses.go | 4 +- cmd/garm-cli/cmd/github_credentials.go | 6 +- cmd/garm-cli/cmd/github_endpoints.go | 4 +- database/common/mocks/Store.go | 144 ++++---- database/common/store.go | 34 +- database/sql/enterprise_test.go | 26 +- database/sql/github.go | 38 +- database/sql/github_test.go | 30 +- database/sql/jobs.go | 8 +- database/sql/models.go | 6 +- database/sql/organizations_test.go | 26 +- database/sql/pools.go | 50 +-- database/sql/repositories_test.go | 26 +- database/sql/scalesets.go | 28 +- database/sql/scalesets_test.go | 8 +- database/sql/sql.go | 8 +- database/sql/util.go | 30 +- database/watcher/filters.go | 46 +-- database/watcher/watcher_store_test.go | 4 +- internal/testing/testing.go | 6 +- params/interfaces.go | 6 +- params/params.go | 329 ++++++++++++------ params/requests.go | 18 +- runner/common/mocks/GithubClient.go | 8 +- runner/common/mocks/GithubEntityOperations.go | 8 +- runner/common/util.go | 2 +- runner/enterprises.go | 24 +- runner/enterprises_test.go | 40 +-- runner/github_endpoints.go | 24 +- runner/metadata.go | 8 +- runner/organizations.go | 24 +- runner/organizations_test.go | 34 +- runner/pool/pool.go | 22 +- runner/pool/stub_client.go | 4 +- runner/pool/util.go | 4 +- runner/pool/watcher.go | 18 +- runner/pools_test.go | 6 +- runner/repositories.go | 24 +- runner/repositories_test.go | 38 +- runner/runner.go | 10 +- runner/scalesets.go | 12 +- test/integration/client_utils.go | 8 +- test/integration/credentials_test.go | 16 +- test/integration/endpoints.go | 2 +- test/integration/endpoints_test.go | 8 +- test/integration/repositories_test.go | 2 +- util/github/client.go | 122 +++++-- util/github/scalesets/token.go | 2 +- workers/cache/cache.go | 2 +- workers/cache/tool_cache.go | 4 +- workers/entity/util.go | 4 +- workers/entity/worker.go | 7 +- workers/entity/worker_watcher.go | 17 +- workers/pools/controller.go | 2 - workers/provider/instance_manager.go | 12 +- workers/provider/provider_helper.go | 8 +- workers/scaleset/controller.go | 4 +- workers/scaleset/controller_watcher.go | 6 +- workers/scaleset/scaleset_helper.go | 6 +- workers/scaleset/util.go | 2 +- 72 files changed, 896 insertions(+), 700 deletions(-) diff --git a/apiserver/controllers/enterprises.go b/apiserver/controllers/enterprises.go index 9be1f1bc..9ce278cd 100644 --- a/apiserver/controllers/enterprises.go +++ b/apiserver/controllers/enterprises.go @@ -320,7 +320,7 @@ func (a *APIController) CreateEnterpriseScaleSetHandler(w http.ResponseWriter, r return } - scaleSet, err := a.r.CreateEntityScaleSet(ctx, runnerParams.GithubEntityTypeEnterprise, enterpriseID, scaleSetData) + scaleSet, err := a.r.CreateEntityScaleSet(ctx, runnerParams.ForgeEntityTypeEnterprise, enterpriseID, scaleSetData) if err != nil { slog.With(slog.Any("error", err)).ErrorContext(ctx, "error creating enterprise scale set") handleError(ctx, w, err) @@ -404,7 +404,7 @@ func (a *APIController) ListEnterpriseScaleSetsHandler(w http.ResponseWriter, r return } - scaleSets, err := a.r.ListEntityScaleSets(ctx, runnerParams.GithubEntityTypeEnterprise, enterpriseID) + scaleSets, err := a.r.ListEntityScaleSets(ctx, runnerParams.ForgeEntityTypeEnterprise, enterpriseID) if err != nil { slog.With(slog.Any("error", err)).ErrorContext(ctx, "listing scale sets") handleError(ctx, w, err) diff --git a/apiserver/controllers/organizations.go b/apiserver/controllers/organizations.go index 149dd490..86f3c5d6 100644 --- a/apiserver/controllers/organizations.go +++ b/apiserver/controllers/organizations.go @@ -330,7 +330,7 @@ func (a *APIController) CreateOrgScaleSetHandler(w http.ResponseWriter, r *http. return } - scaleSet, err := a.r.CreateEntityScaleSet(ctx, runnerParams.GithubEntityTypeOrganization, orgID, scalesetData) + scaleSet, err := a.r.CreateEntityScaleSet(ctx, runnerParams.ForgeEntityTypeOrganization, orgID, scalesetData) if err != nil { slog.With(slog.Any("error", err)).ErrorContext(ctx, "error creating organization scale set") handleError(ctx, w, err) @@ -414,7 +414,7 @@ func (a *APIController) ListOrgScaleSetsHandler(w http.ResponseWriter, r *http.R return } - scaleSets, err := a.r.ListEntityScaleSets(ctx, runnerParams.GithubEntityTypeOrganization, orgID) + scaleSets, err := a.r.ListEntityScaleSets(ctx, runnerParams.ForgeEntityTypeOrganization, orgID) if err != nil { slog.With(slog.Any("error", err)).ErrorContext(ctx, "listing scale sets") handleError(ctx, w, err) diff --git a/apiserver/controllers/repositories.go b/apiserver/controllers/repositories.go index 14693aac..2eea0001 100644 --- a/apiserver/controllers/repositories.go +++ b/apiserver/controllers/repositories.go @@ -329,7 +329,7 @@ func (a *APIController) CreateRepoScaleSetHandler(w http.ResponseWriter, r *http return } - scaleSet, err := a.r.CreateEntityScaleSet(ctx, runnerParams.GithubEntityTypeRepository, repoID, scaleSetData) + scaleSet, err := a.r.CreateEntityScaleSet(ctx, runnerParams.ForgeEntityTypeRepository, repoID, scaleSetData) if err != nil { slog.With(slog.Any("error", err)).ErrorContext(ctx, "error creating repository scale set") handleError(ctx, w, err) @@ -413,7 +413,7 @@ func (a *APIController) ListRepoScaleSetsHandler(w http.ResponseWriter, r *http. return } - scaleSets, err := a.r.ListEntityScaleSets(ctx, runnerParams.GithubEntityTypeRepository, repoID) + scaleSets, err := a.r.ListEntityScaleSets(ctx, runnerParams.ForgeEntityTypeRepository, repoID) if err != nil { slog.With(slog.Any("error", err)).ErrorContext(ctx, "listing scale sets") handleError(ctx, w, err) diff --git a/auth/instance_middleware.go b/auth/instance_middleware.go index dbd3cfb7..38e1fdcf 100644 --- a/auth/instance_middleware.go +++ b/auth/instance_middleware.go @@ -40,7 +40,7 @@ type InstanceJWTClaims struct { Name string `json:"name"` PoolID string `json:"provider_id"` // Scope is either repository or organization - Scope params.GithubEntityType `json:"scope"` + Scope params.ForgeEntityType `json:"scope"` // Entity is the repo or org name Entity string `json:"entity"` CreateAttempt int `json:"create_attempt"` @@ -60,7 +60,7 @@ type instanceToken struct { jwtSecret string } -func (i *instanceToken) NewInstanceJWTToken(instance params.Instance, entity string, entityType params.GithubEntityType, ttlMinutes uint) (string, error) { +func (i *instanceToken) NewInstanceJWTToken(instance params.Instance, entity string, entityType params.ForgeEntityType, 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. diff --git a/auth/interfaces.go b/auth/interfaces.go index 4e4d370c..095db4b2 100644 --- a/auth/interfaces.go +++ b/auth/interfaces.go @@ -26,5 +26,5 @@ type Middleware interface { } type InstanceTokenGetter interface { - NewInstanceJWTToken(instance params.Instance, entity string, poolType params.GithubEntityType, ttlMinutes uint) (string, error) + NewInstanceJWTToken(instance params.Instance, entity string, poolType params.ForgeEntityType, ttlMinutes uint) (string, error) } diff --git a/cache/cache_test.go b/cache/cache_test.go index 7a977394..08b269b8 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -13,13 +13,13 @@ import ( type CacheTestSuite struct { suite.Suite - entity params.GithubEntity + entity params.ForgeEntity } func (c *CacheTestSuite) SetupTest() { - c.entity = params.GithubEntity{ + c.entity = params.ForgeEntity{ ID: "1234", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -254,9 +254,9 @@ func (c *CacheTestSuite) TestGetInstancesForScaleSet() { } func (c *CacheTestSuite) TestSetGetEntityCache() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -265,22 +265,25 @@ func (c *CacheTestSuite) TestSetGetEntityCache() { c.Require().True(ok) c.Require().Equal(entity.ID, cachedEntity.ID) - entity.Credentials.Description = "test description" + entity.Credentials.GithubCredentials.Description = "test description" SetEntity(entity) cachedEntity, ok = GetEntity("test-entity") c.Require().True(ok) c.Require().Equal(entity.ID, cachedEntity.ID) - c.Require().Equal(entity.Credentials.Description, cachedEntity.Credentials.Description) + c.Require().Equal(entity.Credentials.GithubCredentials.Description, cachedEntity.Credentials.GithubCredentials.Description) } func (c *CacheTestSuite) TestReplaceEntityPools() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", - Credentials: params.GithubCredentials{ - ID: 1, + Credentials: params.ForgeCredentials{ + ForgeType: params.GithubEndpointType, + GithubCredentials: params.GithubCredentials{ + ID: 1, + }, }, } pool1 := params.Pool{ @@ -301,7 +304,7 @@ func (c *CacheTestSuite) TestReplaceEntityPools() { cachedEntity, ok := GetEntity(entity.ID) c.Require().True(ok) c.Require().Equal(entity.ID, cachedEntity.ID) - c.Require().Equal("test", cachedEntity.Credentials.Name) + c.Require().Equal("test", cachedEntity.Credentials.GithubCredentials.Name) pools := GetEntityPools(entity.ID) c.Require().Len(pools, 2) @@ -310,9 +313,9 @@ func (c *CacheTestSuite) TestReplaceEntityPools() { } func (c *CacheTestSuite) TestReplaceEntityScaleSets() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -336,9 +339,9 @@ func (c *CacheTestSuite) TestReplaceEntityScaleSets() { } func (c *CacheTestSuite) TestDeleteEntity() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -350,13 +353,13 @@ func (c *CacheTestSuite) TestDeleteEntity() { DeleteEntity(entity.ID) cachedEntity, ok = GetEntity(entity.ID) c.Require().False(ok) - c.Require().Equal(params.GithubEntity{}, cachedEntity) + c.Require().Equal(params.ForgeEntity{}, cachedEntity) } func (c *CacheTestSuite) TestSetEntityPool() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -387,9 +390,9 @@ func (c *CacheTestSuite) TestSetEntityPool() { } func (c *CacheTestSuite) TestSetEntityScaleSet() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -417,9 +420,9 @@ func (c *CacheTestSuite) TestSetEntityScaleSet() { } func (c *CacheTestSuite) TestDeleteEntityPool() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -440,9 +443,9 @@ func (c *CacheTestSuite) TestDeleteEntityPool() { } func (c *CacheTestSuite) TestDeleteEntityScaleSet() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -463,9 +466,9 @@ func (c *CacheTestSuite) TestDeleteEntityScaleSet() { } func (c *CacheTestSuite) TestFindPoolsMatchingAllTags() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -520,9 +523,9 @@ func (c *CacheTestSuite) TestFindPoolsMatchingAllTags() { } func (c *CacheTestSuite) TestGetEntityPools() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -562,9 +565,9 @@ func (c *CacheTestSuite) TestGetEntityPools() { } func (c *CacheTestSuite) TestGetEntityScaleSet() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } @@ -584,9 +587,9 @@ func (c *CacheTestSuite) TestGetEntityScaleSet() { } func (c *CacheTestSuite) TestGetEntityPool() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "test-entity", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", } diff --git a/cache/credentials_cache.go b/cache/credentials_cache.go index 7cf65a03..060b076a 100644 --- a/cache/credentials_cache.go +++ b/cache/credentials_cache.go @@ -36,7 +36,7 @@ func (g *GithubCredentials) SetCredentials(credentials params.GithubCredentials) defer g.mux.Unlock() g.cache[credentials.ID] = credentials - UpdateCredentialsInAffectedEntities(credentials) + UpdateCredentialsInAffectedEntities(credentials.GetForgeCredentials()) } func (g *GithubCredentials) GetCredentials(id uint) (params.GithubCredentials, bool) { diff --git a/cache/entity_cache.go b/cache/entity_cache.go index 006f40db..74f406de 100644 --- a/cache/entity_cache.go +++ b/cache/entity_cache.go @@ -16,7 +16,7 @@ func init() { } type EntityItem struct { - Entity params.GithubEntity + Entity params.ForgeEntity Pools map[string]params.Pool ScaleSets map[uint]params.ScaleSet } @@ -27,34 +27,42 @@ type EntityCache struct { entities map[string]EntityItem } -func (e *EntityCache) UpdateCredentialsInAffectedEntities(creds params.GithubCredentials) { +func (e *EntityCache) UpdateCredentialsInAffectedEntities(creds params.ForgeCredentials) { e.mux.Lock() defer e.mux.Unlock() for entityID, cache := range e.entities { - if cache.Entity.Credentials.ID == creds.ID { + if cache.Entity.Credentials.GetID() == creds.GetID() { cache.Entity.Credentials = creds e.entities[entityID] = cache } } } -func (e *EntityCache) GetEntity(entityID string) (params.GithubEntity, bool) { +func (e *EntityCache) GetEntity(entityID string) (params.ForgeEntity, bool) { e.mux.Lock() defer e.mux.Unlock() if cache, ok := e.entities[entityID]; ok { // Get the credentials from the credentials cache. - creds, ok := GetGithubCredentials(cache.Entity.Credentials.ID) - if ok { - cache.Entity.Credentials = creds + var forgeCredsGetter params.ForgeCredentialsGetter + var credsOk bool + switch cache.Entity.Credentials.ForgeType { + case params.GithubEndpointType: + forgeCredsGetter, credsOk = GetGithubCredentials(cache.Entity.Credentials.GetID()) + case params.GiteaEndpointType: + // add gitea credentials getter + return cache.Entity, false + } + if credsOk { + cache.Entity.Credentials = forgeCredsGetter.GetForgeCredentials() } return cache.Entity, true } - return params.GithubEntity{}, false + return params.ForgeEntity{}, false } -func (e *EntityCache) SetEntity(entity params.GithubEntity) { +func (e *EntityCache) SetEntity(entity params.ForgeEntity) { e.mux.Lock() defer e.mux.Unlock() @@ -225,13 +233,13 @@ func (e *EntityCache) GetEntityScaleSets(entityID string) []params.ScaleSet { return nil } -func (e *EntityCache) GetEntitiesUsingGredentials(credsID uint) []params.GithubEntity { +func (e *EntityCache) GetEntitiesUsingGredentials(credsID uint) []params.ForgeEntity { e.mux.Lock() defer e.mux.Unlock() - var entities []params.GithubEntity + var entities []params.ForgeEntity for _, cache := range e.entities { - if cache.Entity.Credentials.ID == credsID { + if cache.Entity.Credentials.GetID() == credsID { entities = append(entities, cache.Entity) } } @@ -239,16 +247,24 @@ func (e *EntityCache) GetEntitiesUsingGredentials(credsID uint) []params.GithubE return entities } -func (e *EntityCache) GetAllEntities() []params.GithubEntity { +func (e *EntityCache) GetAllEntities() []params.ForgeEntity { e.mux.Lock() defer e.mux.Unlock() - var entities []params.GithubEntity + var entities []params.ForgeEntity for _, cache := range e.entities { // Get the credentials from the credentials cache. - creds, ok := GetGithubCredentials(cache.Entity.Credentials.ID) - if ok { - cache.Entity.Credentials = creds + var forgeCredsGetter params.ForgeCredentialsGetter + var credsOk bool + switch cache.Entity.Credentials.ForgeType { + case params.GithubEndpointType: + forgeCredsGetter, credsOk = GetGithubCredentials(cache.Entity.Credentials.GetID()) + case params.GiteaEndpointType: + // add gitea credentials getter + return nil + } + if credsOk { + cache.Entity.Credentials = forgeCredsGetter.GetForgeCredentials() } entities = append(entities, cache.Entity) } @@ -284,11 +300,11 @@ func (e *EntityCache) GetAllScaleSets() []params.ScaleSet { return scaleSets } -func GetEntity(entityID string) (params.GithubEntity, bool) { +func GetEntity(entityID string) (params.ForgeEntity, bool) { return entityCache.GetEntity(entityID) } -func SetEntity(entity params.GithubEntity) { +func SetEntity(entity params.ForgeEntity) { entityCache.SetEntity(entity) } @@ -340,15 +356,15 @@ func GetEntityScaleSets(entityID string) []params.ScaleSet { return entityCache.GetEntityScaleSets(entityID) } -func UpdateCredentialsInAffectedEntities(creds params.GithubCredentials) { +func UpdateCredentialsInAffectedEntities(creds params.ForgeCredentials) { entityCache.UpdateCredentialsInAffectedEntities(creds) } -func GetEntitiesUsingGredentials(credsID uint) []params.GithubEntity { +func GetEntitiesUsingGredentials(credsID uint) []params.ForgeEntity { return entityCache.GetEntitiesUsingGredentials(credsID) } -func GetAllEntities() []params.GithubEntity { +func GetAllEntities() []params.ForgeEntity { return entityCache.GetAllEntities() } diff --git a/cache/tools_cache.go b/cache/tools_cache.go index 233de2c1..f4a2db62 100644 --- a/cache/tools_cache.go +++ b/cache/tools_cache.go @@ -19,7 +19,7 @@ func init() { type GithubEntityTools struct { updatedAt time.Time - entity params.GithubEntity + entity params.ForgeEntity tools []commonParams.RunnerApplicationDownload } @@ -44,7 +44,7 @@ func (g *GithubToolsCache) Get(entityID string) ([]commonParams.RunnerApplicatio return nil, false } -func (g *GithubToolsCache) Set(entity params.GithubEntity, tools []commonParams.RunnerApplicationDownload) { +func (g *GithubToolsCache) Set(entity params.ForgeEntity, tools []commonParams.RunnerApplicationDownload) { g.mux.Lock() defer g.mux.Unlock() @@ -55,7 +55,7 @@ func (g *GithubToolsCache) Set(entity params.GithubEntity, tools []commonParams. } } -func SetGithubToolsCache(entity params.GithubEntity, tools []commonParams.RunnerApplicationDownload) { +func SetGithubToolsCache(entity params.ForgeEntity, tools []commonParams.RunnerApplicationDownload) { githubToolsCache.Set(entity, tools) } diff --git a/client/endpoints/create_github_endpoint_responses.go b/client/endpoints/create_github_endpoint_responses.go index acd95088..57016978 100644 --- a/client/endpoints/create_github_endpoint_responses.go +++ b/client/endpoints/create_github_endpoint_responses.go @@ -54,7 +54,7 @@ CreateGithubEndpointOK describes a response with status code 200, with default h GithubEndpoint */ type CreateGithubEndpointOK struct { - Payload garm_params.GithubEndpoint + Payload garm_params.ForgeEndpoint } // IsSuccess returns true when this create github endpoint o k response has a 2xx status code @@ -97,7 +97,7 @@ func (o *CreateGithubEndpointOK) String() string { return fmt.Sprintf("[POST /github/endpoints][%d] createGithubEndpointOK %s", 200, payload) } -func (o *CreateGithubEndpointOK) GetPayload() garm_params.GithubEndpoint { +func (o *CreateGithubEndpointOK) GetPayload() garm_params.ForgeEndpoint { return o.Payload } diff --git a/client/endpoints/get_github_endpoint_responses.go b/client/endpoints/get_github_endpoint_responses.go index d84f9280..4b4881cd 100644 --- a/client/endpoints/get_github_endpoint_responses.go +++ b/client/endpoints/get_github_endpoint_responses.go @@ -54,7 +54,7 @@ GetGithubEndpointOK describes a response with status code 200, with default head GithubEndpoint */ type GetGithubEndpointOK struct { - Payload garm_params.GithubEndpoint + Payload garm_params.ForgeEndpoint } // IsSuccess returns true when this get github endpoint o k response has a 2xx status code @@ -97,7 +97,7 @@ func (o *GetGithubEndpointOK) String() string { return fmt.Sprintf("[GET /github/endpoints/{name}][%d] getGithubEndpointOK %s", 200, payload) } -func (o *GetGithubEndpointOK) GetPayload() garm_params.GithubEndpoint { +func (o *GetGithubEndpointOK) GetPayload() garm_params.ForgeEndpoint { return o.Payload } diff --git a/client/endpoints/list_github_endpoints_responses.go b/client/endpoints/list_github_endpoints_responses.go index 6c2dde6c..f7b10a1c 100644 --- a/client/endpoints/list_github_endpoints_responses.go +++ b/client/endpoints/list_github_endpoints_responses.go @@ -54,7 +54,7 @@ ListGithubEndpointsOK describes a response with status code 200, with default he GithubEndpoints */ type ListGithubEndpointsOK struct { - Payload garm_params.GithubEndpoints + Payload garm_params.ForgeEndpoints } // IsSuccess returns true when this list github endpoints o k response has a 2xx status code @@ -97,7 +97,7 @@ func (o *ListGithubEndpointsOK) String() string { return fmt.Sprintf("[GET /github/endpoints][%d] listGithubEndpointsOK %s", 200, payload) } -func (o *ListGithubEndpointsOK) GetPayload() garm_params.GithubEndpoints { +func (o *ListGithubEndpointsOK) GetPayload() garm_params.ForgeEndpoints { return o.Payload } diff --git a/client/endpoints/update_github_endpoint_responses.go b/client/endpoints/update_github_endpoint_responses.go index 234ed711..969d6bbd 100644 --- a/client/endpoints/update_github_endpoint_responses.go +++ b/client/endpoints/update_github_endpoint_responses.go @@ -54,7 +54,7 @@ UpdateGithubEndpointOK describes a response with status code 200, with default h GithubEndpoint */ type UpdateGithubEndpointOK struct { - Payload garm_params.GithubEndpoint + Payload garm_params.ForgeEndpoint } // IsSuccess returns true when this update github endpoint o k response has a 2xx status code @@ -97,7 +97,7 @@ func (o *UpdateGithubEndpointOK) String() string { return fmt.Sprintf("[PUT /github/endpoints/{name}][%d] updateGithubEndpointOK %s", 200, payload) } -func (o *UpdateGithubEndpointOK) GetPayload() garm_params.GithubEndpoint { +func (o *UpdateGithubEndpointOK) GetPayload() garm_params.ForgeEndpoint { return o.Payload } diff --git a/cmd/garm-cli/cmd/github_credentials.go b/cmd/garm-cli/cmd/github_credentials.go index 2b2128d0..fb3853d8 100644 --- a/cmd/garm-cli/cmd/github_credentials.go +++ b/cmd/garm-cli/cmd/github_credentials.go @@ -283,12 +283,12 @@ func parsePrivateKeyFromPath(path string) ([]byte, error) { func parseCredentialsAddParams() (ret params.CreateGithubCredentialsParams, err error) { ret.Name = credentialsName ret.Description = credentialsDescription - ret.AuthType = params.GithubAuthType(credentialsType) + ret.AuthType = params.ForgeAuthType(credentialsType) ret.Endpoint = credentialsEndpoint switch ret.AuthType { - case params.GithubAuthTypePAT: + case params.ForgeAuthTypePAT: ret.PAT.OAuth2Token = credentialsOAuthToken - case params.GithubAuthTypeApp: + case params.ForgeAuthTypeApp: ret.App.InstallationID = credentialsAppInstallationID ret.App.AppID = credentialsAppID keyContents, err := parsePrivateKeyFromPath(credentialsPrivateKeyPath) diff --git a/cmd/garm-cli/cmd/github_endpoints.go b/cmd/garm-cli/cmd/github_endpoints.go index 2be14f52..f119a1a2 100644 --- a/cmd/garm-cli/cmd/github_endpoints.go +++ b/cmd/garm-cli/cmd/github_endpoints.go @@ -252,7 +252,7 @@ func parseCreateParams() (params.CreateGithubEndpointParams, error) { return ret, nil } -func formatEndpoints(endpoints params.GithubEndpoints) { +func formatEndpoints(endpoints params.ForgeEndpoints) { if outputFormat == common.OutputFormatJSON { printAsJSON(endpoints) return @@ -274,7 +274,7 @@ func formatEndpoints(endpoints params.GithubEndpoints) { fmt.Println(t.Render()) } -func formatOneEndpoint(endpoint params.GithubEndpoint) { +func formatOneEndpoint(endpoint params.ForgeEndpoint) { if outputFormat == common.OutputFormatJSON { printAsJSON(endpoint) return diff --git a/database/common/mocks/Store.go b/database/common/mocks/Store.go index f7f508b5..53b90720 100644 --- a/database/common/mocks/Store.go +++ b/database/common/mocks/Store.go @@ -15,7 +15,7 @@ type Store struct { } // AddEntityEvent provides a mock function with given fields: ctx, entity, event, eventLevel, statusMessage, maxEvents -func (_m *Store) AddEntityEvent(ctx context.Context, entity params.GithubEntity, event params.EventType, eventLevel params.EventLevel, statusMessage string, maxEvents int) error { +func (_m *Store) AddEntityEvent(ctx context.Context, entity params.ForgeEntity, event params.EventType, eventLevel params.EventLevel, statusMessage string, maxEvents int) error { ret := _m.Called(ctx, entity, event, eventLevel, statusMessage, maxEvents) if len(ret) == 0 { @@ -23,7 +23,7 @@ func (_m *Store) AddEntityEvent(ctx context.Context, entity params.GithubEntity, } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, params.EventType, params.EventLevel, string, int) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, params.EventType, params.EventLevel, string, int) error); ok { r0 = rf(ctx, entity, event, eventLevel, statusMessage, maxEvents) } else { r0 = ret.Error(0) @@ -125,7 +125,7 @@ func (_m *Store) CreateEnterprise(ctx context.Context, name string, credentialsN } // CreateEntityPool provides a mock function with given fields: ctx, entity, param -func (_m *Store) CreateEntityPool(ctx context.Context, entity params.GithubEntity, param params.CreatePoolParams) (params.Pool, error) { +func (_m *Store) CreateEntityPool(ctx context.Context, entity params.ForgeEntity, param params.CreatePoolParams) (params.Pool, error) { ret := _m.Called(ctx, entity, param) if len(ret) == 0 { @@ -134,16 +134,16 @@ func (_m *Store) CreateEntityPool(ctx context.Context, entity params.GithubEntit var r0 params.Pool var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, params.CreatePoolParams) (params.Pool, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, params.CreatePoolParams) (params.Pool, error)); ok { return rf(ctx, entity, param) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, params.CreatePoolParams) params.Pool); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, params.CreatePoolParams) params.Pool); ok { r0 = rf(ctx, entity, param) } else { r0 = ret.Get(0).(params.Pool) } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntity, params.CreatePoolParams) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntity, params.CreatePoolParams) error); ok { r1 = rf(ctx, entity, param) } else { r1 = ret.Error(1) @@ -153,7 +153,7 @@ func (_m *Store) CreateEntityPool(ctx context.Context, entity params.GithubEntit } // CreateEntityScaleSet provides a mock function with given fields: _a0, entity, param -func (_m *Store) CreateEntityScaleSet(_a0 context.Context, entity params.GithubEntity, param params.CreateScaleSetParams) (params.ScaleSet, error) { +func (_m *Store) CreateEntityScaleSet(_a0 context.Context, entity params.ForgeEntity, param params.CreateScaleSetParams) (params.ScaleSet, error) { ret := _m.Called(_a0, entity, param) if len(ret) == 0 { @@ -162,16 +162,16 @@ func (_m *Store) CreateEntityScaleSet(_a0 context.Context, entity params.GithubE var r0 params.ScaleSet var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, params.CreateScaleSetParams) (params.ScaleSet, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, params.CreateScaleSetParams) (params.ScaleSet, error)); ok { return rf(_a0, entity, param) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, params.CreateScaleSetParams) params.ScaleSet); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, params.CreateScaleSetParams) params.ScaleSet); ok { r0 = rf(_a0, entity, param) } else { r0 = ret.Get(0).(params.ScaleSet) } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntity, params.CreateScaleSetParams) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntity, params.CreateScaleSetParams) error); ok { r1 = rf(_a0, entity, param) } else { r1 = ret.Error(1) @@ -209,22 +209,22 @@ func (_m *Store) CreateGithubCredentials(ctx context.Context, param params.Creat } // CreateGithubEndpoint provides a mock function with given fields: ctx, param -func (_m *Store) CreateGithubEndpoint(ctx context.Context, param params.CreateGithubEndpointParams) (params.GithubEndpoint, error) { +func (_m *Store) CreateGithubEndpoint(ctx context.Context, param params.CreateGithubEndpointParams) (params.ForgeEndpoint, error) { ret := _m.Called(ctx, param) if len(ret) == 0 { panic("no return value specified for CreateGithubEndpoint") } - var r0 params.GithubEndpoint + var r0 params.ForgeEndpoint var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.CreateGithubEndpointParams) (params.GithubEndpoint, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.CreateGithubEndpointParams) (params.ForgeEndpoint, error)); ok { return rf(ctx, param) } - if rf, ok := ret.Get(0).(func(context.Context, params.CreateGithubEndpointParams) params.GithubEndpoint); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.CreateGithubEndpointParams) params.ForgeEndpoint); ok { r0 = rf(ctx, param) } else { - r0 = ret.Get(0).(params.GithubEndpoint) + r0 = ret.Get(0).(params.ForgeEndpoint) } if rf, ok := ret.Get(1).(func(context.Context, params.CreateGithubEndpointParams) error); ok { @@ -441,7 +441,7 @@ func (_m *Store) DeleteEnterprise(ctx context.Context, enterpriseID string) erro } // DeleteEntityPool provides a mock function with given fields: ctx, entity, poolID -func (_m *Store) DeleteEntityPool(ctx context.Context, entity params.GithubEntity, poolID string) error { +func (_m *Store) DeleteEntityPool(ctx context.Context, entity params.ForgeEntity, poolID string) error { ret := _m.Called(ctx, entity, poolID) if len(ret) == 0 { @@ -449,7 +449,7 @@ func (_m *Store) DeleteEntityPool(ctx context.Context, entity params.GithubEntit } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, string) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, string) error); ok { r0 = rf(ctx, entity, poolID) } else { r0 = ret.Error(0) @@ -621,7 +621,7 @@ func (_m *Store) DeleteScaleSetByID(ctx context.Context, scaleSetID uint) error } // FindPoolsMatchingAllTags provides a mock function with given fields: ctx, entityType, entityID, tags -func (_m *Store) FindPoolsMatchingAllTags(ctx context.Context, entityType params.GithubEntityType, entityID string, tags []string) ([]params.Pool, error) { +func (_m *Store) FindPoolsMatchingAllTags(ctx context.Context, entityType params.ForgeEntityType, entityID string, tags []string) ([]params.Pool, error) { ret := _m.Called(ctx, entityType, entityID, tags) if len(ret) == 0 { @@ -630,10 +630,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.GithubEntityType, string, []string) ([]params.Pool, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string, []string) ([]params.Pool, error)); ok { return rf(ctx, entityType, entityID, tags) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntityType, string, []string) []params.Pool); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string, []string) []params.Pool); ok { r0 = rf(ctx, entityType, entityID, tags) } else { if ret.Get(0) != nil { @@ -641,7 +641,7 @@ func (_m *Store) FindPoolsMatchingAllTags(ctx context.Context, entityType params } } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntityType, string, []string) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntityType, string, []string) error); ok { r1 = rf(ctx, entityType, entityID, tags) } else { r1 = ret.Error(1) @@ -735,7 +735,7 @@ func (_m *Store) GetEnterpriseByID(ctx context.Context, enterpriseID string) (pa } // GetEntityPool provides a mock function with given fields: ctx, entity, poolID -func (_m *Store) GetEntityPool(ctx context.Context, entity params.GithubEntity, poolID string) (params.Pool, error) { +func (_m *Store) GetEntityPool(ctx context.Context, entity params.ForgeEntity, poolID string) (params.Pool, error) { ret := _m.Called(ctx, entity, poolID) if len(ret) == 0 { @@ -744,16 +744,16 @@ func (_m *Store) GetEntityPool(ctx context.Context, entity params.GithubEntity, var r0 params.Pool var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, string) (params.Pool, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, string) (params.Pool, error)); ok { return rf(ctx, entity, poolID) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, string) params.Pool); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, string) params.Pool); ok { r0 = rf(ctx, entity, poolID) } else { r0 = ret.Get(0).(params.Pool) } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntity, string) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntity, string) error); ok { r1 = rf(ctx, entity, poolID) } else { r1 = ret.Error(1) @@ -819,22 +819,22 @@ func (_m *Store) GetGithubCredentialsByName(ctx context.Context, name string, de } // GetGithubEndpoint provides a mock function with given fields: ctx, name -func (_m *Store) GetGithubEndpoint(ctx context.Context, name string) (params.GithubEndpoint, error) { +func (_m *Store) GetGithubEndpoint(ctx context.Context, name string) (params.ForgeEndpoint, error) { ret := _m.Called(ctx, name) if len(ret) == 0 { panic("no return value specified for GetGithubEndpoint") } - var r0 params.GithubEndpoint + var r0 params.ForgeEndpoint var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (params.GithubEndpoint, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) (params.ForgeEndpoint, error)); ok { return rf(ctx, name) } - if rf, ok := ret.Get(0).(func(context.Context, string) params.GithubEndpoint); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) params.ForgeEndpoint); ok { r0 = rf(ctx, name) } else { - r0 = ret.Get(0).(params.GithubEndpoint) + r0 = ret.Get(0).(params.ForgeEndpoint) } if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { @@ -846,26 +846,26 @@ func (_m *Store) GetGithubEndpoint(ctx context.Context, name string) (params.Git return r0, r1 } -// GetGithubEntity provides a mock function with given fields: _a0, entityType, entityID -func (_m *Store) GetGithubEntity(_a0 context.Context, entityType params.GithubEntityType, entityID string) (params.GithubEntity, error) { +// GetForgeEntity provides a mock function with given fields: _a0, entityType, entityID +func (_m *Store) GetForgeEntity(_a0 context.Context, entityType params.ForgeEntityType, entityID string) (params.ForgeEntity, error) { ret := _m.Called(_a0, entityType, entityID) if len(ret) == 0 { - panic("no return value specified for GetGithubEntity") + panic("no return value specified for GetForgeEntity") } - var r0 params.GithubEntity + var r0 params.ForgeEntity var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntityType, string) (params.GithubEntity, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string) (params.ForgeEntity, error)); ok { return rf(_a0, entityType, entityID) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntityType, string) params.GithubEntity); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string) params.ForgeEntity); ok { r0 = rf(_a0, entityType, entityID) } else { - r0 = ret.Get(0).(params.GithubEntity) + r0 = ret.Get(0).(params.ForgeEntity) } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntityType, string) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntityType, string) error); ok { r1 = rf(_a0, entityType, entityID) } else { r1 = ret.Error(1) @@ -1379,7 +1379,7 @@ func (_m *Store) ListEnterprises(ctx context.Context) ([]params.Enterprise, erro } // ListEntityInstances provides a mock function with given fields: ctx, entity -func (_m *Store) ListEntityInstances(ctx context.Context, entity params.GithubEntity) ([]params.Instance, error) { +func (_m *Store) ListEntityInstances(ctx context.Context, entity params.ForgeEntity) ([]params.Instance, error) { ret := _m.Called(ctx, entity) if len(ret) == 0 { @@ -1388,10 +1388,10 @@ func (_m *Store) ListEntityInstances(ctx context.Context, entity params.GithubEn var r0 []params.Instance var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity) ([]params.Instance, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity) ([]params.Instance, error)); ok { return rf(ctx, entity) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity) []params.Instance); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity) []params.Instance); ok { r0 = rf(ctx, entity) } else { if ret.Get(0) != nil { @@ -1399,7 +1399,7 @@ func (_m *Store) ListEntityInstances(ctx context.Context, entity params.GithubEn } } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntity) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntity) error); ok { r1 = rf(ctx, entity) } else { r1 = ret.Error(1) @@ -1409,7 +1409,7 @@ func (_m *Store) ListEntityInstances(ctx context.Context, entity params.GithubEn } // ListEntityJobsByStatus provides a mock function with given fields: ctx, entityType, entityID, status -func (_m *Store) ListEntityJobsByStatus(ctx context.Context, entityType params.GithubEntityType, entityID string, status params.JobStatus) ([]params.Job, error) { +func (_m *Store) ListEntityJobsByStatus(ctx context.Context, entityType params.ForgeEntityType, entityID string, status params.JobStatus) ([]params.Job, error) { ret := _m.Called(ctx, entityType, entityID, status) if len(ret) == 0 { @@ -1418,10 +1418,10 @@ func (_m *Store) ListEntityJobsByStatus(ctx context.Context, entityType params.G var r0 []params.Job var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntityType, string, params.JobStatus) ([]params.Job, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string, params.JobStatus) ([]params.Job, error)); ok { return rf(ctx, entityType, entityID, status) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntityType, string, params.JobStatus) []params.Job); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string, params.JobStatus) []params.Job); ok { r0 = rf(ctx, entityType, entityID, status) } else { if ret.Get(0) != nil { @@ -1429,7 +1429,7 @@ func (_m *Store) ListEntityJobsByStatus(ctx context.Context, entityType params.G } } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntityType, string, params.JobStatus) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntityType, string, params.JobStatus) error); ok { r1 = rf(ctx, entityType, entityID, status) } else { r1 = ret.Error(1) @@ -1439,7 +1439,7 @@ func (_m *Store) ListEntityJobsByStatus(ctx context.Context, entityType params.G } // ListEntityPools provides a mock function with given fields: ctx, entity -func (_m *Store) ListEntityPools(ctx context.Context, entity params.GithubEntity) ([]params.Pool, error) { +func (_m *Store) ListEntityPools(ctx context.Context, entity params.ForgeEntity) ([]params.Pool, error) { ret := _m.Called(ctx, entity) if len(ret) == 0 { @@ -1448,10 +1448,10 @@ func (_m *Store) ListEntityPools(ctx context.Context, entity params.GithubEntity var r0 []params.Pool var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity) ([]params.Pool, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity) ([]params.Pool, error)); ok { return rf(ctx, entity) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity) []params.Pool); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity) []params.Pool); ok { r0 = rf(ctx, entity) } else { if ret.Get(0) != nil { @@ -1459,7 +1459,7 @@ func (_m *Store) ListEntityPools(ctx context.Context, entity params.GithubEntity } } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntity) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntity) error); ok { r1 = rf(ctx, entity) } else { r1 = ret.Error(1) @@ -1469,7 +1469,7 @@ func (_m *Store) ListEntityPools(ctx context.Context, entity params.GithubEntity } // ListEntityScaleSets provides a mock function with given fields: _a0, entity -func (_m *Store) ListEntityScaleSets(_a0 context.Context, entity params.GithubEntity) ([]params.ScaleSet, error) { +func (_m *Store) ListEntityScaleSets(_a0 context.Context, entity params.ForgeEntity) ([]params.ScaleSet, error) { ret := _m.Called(_a0, entity) if len(ret) == 0 { @@ -1478,10 +1478,10 @@ func (_m *Store) ListEntityScaleSets(_a0 context.Context, entity params.GithubEn var r0 []params.ScaleSet var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity) ([]params.ScaleSet, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity) ([]params.ScaleSet, error)); ok { return rf(_a0, entity) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity) []params.ScaleSet); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity) []params.ScaleSet); ok { r0 = rf(_a0, entity) } else { if ret.Get(0) != nil { @@ -1489,7 +1489,7 @@ func (_m *Store) ListEntityScaleSets(_a0 context.Context, entity params.GithubEn } } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntity) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntity) error); ok { r1 = rf(_a0, entity) } else { r1 = ret.Error(1) @@ -1529,23 +1529,23 @@ func (_m *Store) ListGithubCredentials(ctx context.Context) ([]params.GithubCred } // ListGithubEndpoints provides a mock function with given fields: ctx -func (_m *Store) ListGithubEndpoints(ctx context.Context) ([]params.GithubEndpoint, error) { +func (_m *Store) ListGithubEndpoints(ctx context.Context) ([]params.ForgeEndpoint, error) { ret := _m.Called(ctx) if len(ret) == 0 { panic("no return value specified for ListGithubEndpoints") } - var r0 []params.GithubEndpoint + var r0 []params.ForgeEndpoint var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]params.GithubEndpoint, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context) ([]params.ForgeEndpoint, error)); ok { return rf(ctx) } - if rf, ok := ret.Get(0).(func(context.Context) []params.GithubEndpoint); ok { + if rf, ok := ret.Get(0).(func(context.Context) []params.ForgeEndpoint); ok { r0 = rf(ctx) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]params.GithubEndpoint) + r0 = ret.Get(0).([]params.ForgeEndpoint) } } @@ -1865,7 +1865,7 @@ func (_m *Store) UpdateEnterprise(ctx context.Context, enterpriseID string, para } // UpdateEntityPool provides a mock function with given fields: ctx, entity, poolID, param -func (_m *Store) UpdateEntityPool(ctx context.Context, entity params.GithubEntity, poolID string, param params.UpdatePoolParams) (params.Pool, error) { +func (_m *Store) UpdateEntityPool(ctx context.Context, entity params.ForgeEntity, poolID string, param params.UpdatePoolParams) (params.Pool, error) { ret := _m.Called(ctx, entity, poolID, param) if len(ret) == 0 { @@ -1874,16 +1874,16 @@ func (_m *Store) UpdateEntityPool(ctx context.Context, entity params.GithubEntit var r0 params.Pool var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, string, params.UpdatePoolParams) (params.Pool, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, string, params.UpdatePoolParams) (params.Pool, error)); ok { return rf(ctx, entity, poolID, param) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, string, params.UpdatePoolParams) params.Pool); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, string, params.UpdatePoolParams) params.Pool); ok { r0 = rf(ctx, entity, poolID, param) } else { r0 = ret.Get(0).(params.Pool) } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntity, string, params.UpdatePoolParams) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntity, string, params.UpdatePoolParams) error); ok { r1 = rf(ctx, entity, poolID, param) } else { r1 = ret.Error(1) @@ -1893,7 +1893,7 @@ func (_m *Store) UpdateEntityPool(ctx context.Context, entity params.GithubEntit } // UpdateEntityScaleSet provides a mock function with given fields: _a0, entity, scaleSetID, param, callback -func (_m *Store) UpdateEntityScaleSet(_a0 context.Context, entity params.GithubEntity, scaleSetID uint, param params.UpdateScaleSetParams, callback func(params.ScaleSet, params.ScaleSet) error) (params.ScaleSet, error) { +func (_m *Store) UpdateEntityScaleSet(_a0 context.Context, entity params.ForgeEntity, scaleSetID uint, param params.UpdateScaleSetParams, callback func(params.ScaleSet, params.ScaleSet) error) (params.ScaleSet, error) { ret := _m.Called(_a0, entity, scaleSetID, param, callback) if len(ret) == 0 { @@ -1902,16 +1902,16 @@ func (_m *Store) UpdateEntityScaleSet(_a0 context.Context, entity params.GithubE var r0 params.ScaleSet var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, uint, params.UpdateScaleSetParams, func(params.ScaleSet, params.ScaleSet) error) (params.ScaleSet, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, uint, params.UpdateScaleSetParams, func(params.ScaleSet, params.ScaleSet) error) (params.ScaleSet, error)); ok { return rf(_a0, entity, scaleSetID, param, callback) } - if rf, ok := ret.Get(0).(func(context.Context, params.GithubEntity, uint, params.UpdateScaleSetParams, func(params.ScaleSet, params.ScaleSet) error) params.ScaleSet); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntity, uint, params.UpdateScaleSetParams, func(params.ScaleSet, params.ScaleSet) error) params.ScaleSet); ok { r0 = rf(_a0, entity, scaleSetID, param, callback) } else { r0 = ret.Get(0).(params.ScaleSet) } - if rf, ok := ret.Get(1).(func(context.Context, params.GithubEntity, uint, params.UpdateScaleSetParams, func(params.ScaleSet, params.ScaleSet) error) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntity, uint, params.UpdateScaleSetParams, func(params.ScaleSet, params.ScaleSet) error) error); ok { r1 = rf(_a0, entity, scaleSetID, param, callback) } else { r1 = ret.Error(1) @@ -1949,22 +1949,22 @@ func (_m *Store) UpdateGithubCredentials(ctx context.Context, id uint, param par } // UpdateGithubEndpoint provides a mock function with given fields: ctx, name, param -func (_m *Store) UpdateGithubEndpoint(ctx context.Context, name string, param params.UpdateGithubEndpointParams) (params.GithubEndpoint, error) { +func (_m *Store) UpdateGithubEndpoint(ctx context.Context, name string, param params.UpdateGithubEndpointParams) (params.ForgeEndpoint, error) { ret := _m.Called(ctx, name, param) if len(ret) == 0 { panic("no return value specified for UpdateGithubEndpoint") } - var r0 params.GithubEndpoint + var r0 params.ForgeEndpoint var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, params.UpdateGithubEndpointParams) (params.GithubEndpoint, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, string, params.UpdateGithubEndpointParams) (params.ForgeEndpoint, error)); ok { return rf(ctx, name, param) } - if rf, ok := ret.Get(0).(func(context.Context, string, params.UpdateGithubEndpointParams) params.GithubEndpoint); ok { + if rf, ok := ret.Get(0).(func(context.Context, string, params.UpdateGithubEndpointParams) params.ForgeEndpoint); ok { r0 = rf(ctx, name, param) } else { - r0 = ret.Get(0).(params.GithubEndpoint) + r0 = ret.Get(0).(params.ForgeEndpoint) } if rf, ok := ret.Get(1).(func(context.Context, string, params.UpdateGithubEndpointParams) error); ok { diff --git a/database/common/store.go b/database/common/store.go index 65fd1343..e5458eaf 100644 --- a/database/common/store.go +++ b/database/common/store.go @@ -21,10 +21,10 @@ import ( ) type GithubEndpointStore interface { - CreateGithubEndpoint(ctx context.Context, param params.CreateGithubEndpointParams) (params.GithubEndpoint, error) - GetGithubEndpoint(ctx context.Context, name string) (params.GithubEndpoint, error) - ListGithubEndpoints(ctx context.Context) ([]params.GithubEndpoint, error) - UpdateGithubEndpoint(ctx context.Context, name string, param params.UpdateGithubEndpointParams) (params.GithubEndpoint, error) + CreateGithubEndpoint(ctx context.Context, param params.CreateGithubEndpointParams) (params.ForgeEndpoint, error) + GetGithubEndpoint(ctx context.Context, name string) (params.ForgeEndpoint, error) + ListGithubEndpoints(ctx context.Context) ([]params.ForgeEndpoint, error) + UpdateGithubEndpoint(ctx context.Context, name string, param params.UpdateGithubEndpointParams) (params.ForgeEndpoint, error) DeleteGithubEndpoint(ctx context.Context, name string) error } @@ -76,7 +76,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.GithubEntityType, entityID string, tags []string) ([]params.Pool, error) + FindPoolsMatchingAllTags(ctx context.Context, entityType params.ForgeEntityType, entityID string, tags []string) ([]params.Pool, error) } type UserStore interface { @@ -107,7 +107,7 @@ type InstanceStore interface { type JobsStore interface { CreateOrUpdateJob(ctx context.Context, job params.Job) (params.Job, error) - ListEntityJobsByStatus(ctx context.Context, entityType params.GithubEntityType, entityID string, status params.JobStatus) ([]params.Job, error) + ListEntityJobsByStatus(ctx context.Context, entityType params.ForgeEntityType, 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) @@ -121,13 +121,13 @@ type JobsStore interface { } type EntityPoolStore interface { - CreateEntityPool(ctx context.Context, entity params.GithubEntity, param params.CreatePoolParams) (params.Pool, error) - GetEntityPool(ctx context.Context, entity params.GithubEntity, poolID string) (params.Pool, error) - DeleteEntityPool(ctx context.Context, entity params.GithubEntity, poolID string) error - UpdateEntityPool(ctx context.Context, entity params.GithubEntity, poolID string, param params.UpdatePoolParams) (params.Pool, error) + CreateEntityPool(ctx context.Context, entity params.ForgeEntity, param params.CreatePoolParams) (params.Pool, error) + GetEntityPool(ctx context.Context, entity params.ForgeEntity, poolID string) (params.Pool, error) + DeleteEntityPool(ctx context.Context, entity params.ForgeEntity, poolID string) error + UpdateEntityPool(ctx context.Context, entity params.ForgeEntity, poolID string, param params.UpdatePoolParams) (params.Pool, error) - ListEntityPools(ctx context.Context, entity params.GithubEntity) ([]params.Pool, error) - ListEntityInstances(ctx context.Context, entity params.GithubEntity) ([]params.Instance, error) + ListEntityPools(ctx context.Context, entity params.ForgeEntity) ([]params.Pool, error) + ListEntityInstances(ctx context.Context, entity params.ForgeEntity) ([]params.Instance, error) } type ControllerStore interface { @@ -138,9 +138,9 @@ type ControllerStore interface { type ScaleSetsStore interface { ListAllScaleSets(ctx context.Context) ([]params.ScaleSet, error) - CreateEntityScaleSet(_ context.Context, entity params.GithubEntity, param params.CreateScaleSetParams) (scaleSet params.ScaleSet, err error) - ListEntityScaleSets(_ context.Context, entity params.GithubEntity) ([]params.ScaleSet, error) - UpdateEntityScaleSet(_ context.Context, entity params.GithubEntity, scaleSetID uint, param params.UpdateScaleSetParams, callback func(old, newSet params.ScaleSet) error) (updatedScaleSet params.ScaleSet, err error) + CreateEntityScaleSet(_ context.Context, entity params.ForgeEntity, param params.CreateScaleSetParams) (scaleSet params.ScaleSet, err error) + ListEntityScaleSets(_ context.Context, entity params.ForgeEntity) ([]params.ScaleSet, error) + UpdateEntityScaleSet(_ context.Context, entity params.ForgeEntity, scaleSetID uint, param params.UpdateScaleSetParams, callback func(old, newSet params.ScaleSet) error) (updatedScaleSet params.ScaleSet, err error) GetScaleSetByID(ctx context.Context, scaleSet uint) (params.ScaleSet, error) DeleteScaleSetByID(ctx context.Context, scaleSetID uint) (err error) SetScaleSetLastMessageID(ctx context.Context, scaleSetID uint, lastMessageID int64) error @@ -170,6 +170,6 @@ type Store interface { ControllerInfo() (params.ControllerInfo, error) InitController() (params.ControllerInfo, error) - GetGithubEntity(_ context.Context, entityType params.GithubEntityType, entityID string) (params.GithubEntity, error) - AddEntityEvent(ctx context.Context, entity params.GithubEntity, event params.EventType, eventLevel params.EventLevel, statusMessage string, maxEvents int) error + GetForgeEntity(_ context.Context, entityType params.ForgeEntityType, entityID string) (params.ForgeEntity, error) + AddEntityEvent(ctx context.Context, entity params.ForgeEntity, event params.EventType, eventLevel params.EventLevel, statusMessage string, maxEvents int) error } diff --git a/database/sql/enterprise_test.go b/database/sql/enterprise_test.go index 3cfcbc32..3e8f6493 100644 --- a/database/sql/enterprise_test.go +++ b/database/sql/enterprise_test.go @@ -55,7 +55,7 @@ type EnterpriseTestSuite struct { testCreds params.GithubCredentials secondaryTestCreds params.GithubCredentials - githubEndpoint params.GithubEndpoint + githubEndpoint params.ForgeEndpoint } func (s *EnterpriseTestSuite) equalInstancesByName(expected, actual []params.Instance) { @@ -490,9 +490,9 @@ func (s *EnterpriseTestSuite) TestCreateEnterprisePoolMissingTags() { } func (s *EnterpriseTestSuite) TestCreateEnterprisePoolInvalidEnterpriseID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-enterprise-id", - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } _, err := s.Store.CreateEntityPool(s.adminCtx, entity, s.Fixtures.CreatePoolParams) @@ -637,9 +637,9 @@ func (s *EnterpriseTestSuite) TestListEnterprisePools() { } func (s *EnterpriseTestSuite) TestListEnterprisePoolsInvalidEnterpriseID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-enterprise-id", - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } _, err := s.Store.ListEntityPools(s.adminCtx, entity) @@ -662,9 +662,9 @@ func (s *EnterpriseTestSuite) TestGetEnterprisePool() { } func (s *EnterpriseTestSuite) TestGetEnterprisePoolInvalidEnterpriseID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-enterprise-id", - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } _, err := s.Store.GetEntityPool(s.adminCtx, entity, "dummy-pool-id") @@ -688,9 +688,9 @@ func (s *EnterpriseTestSuite) TestDeleteEnterprisePool() { } func (s *EnterpriseTestSuite) TestDeleteEnterprisePoolInvalidEnterpriseID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-enterprise-id", - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } err := s.Store.DeleteEntityPool(s.adminCtx, entity, "dummy-pool-id") @@ -743,9 +743,9 @@ func (s *EnterpriseTestSuite) TestListEnterpriseInstances() { } func (s *EnterpriseTestSuite) TestListEnterpriseInstancesInvalidEnterpriseID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-enterprise-id", - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } _, err := s.Store.ListEntityInstances(s.adminCtx, entity) @@ -771,9 +771,9 @@ func (s *EnterpriseTestSuite) TestUpdateEnterprisePool() { } func (s *EnterpriseTestSuite) TestUpdateEnterprisePoolInvalidEnterpriseID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-enterprise-id", - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } _, err := s.Store.UpdateEntityPool(s.adminCtx, entity, "dummy-pool-id", s.Fixtures.UpdatePoolParams) diff --git a/database/sql/github.go b/database/sql/github.go index 2fb46d65..861c824c 100644 --- a/database/sql/github.go +++ b/database/sql/github.go @@ -88,8 +88,8 @@ func (s *sqlDatabase) sqlToCommonGithubCredentials(creds GithubCredentials) (par return commonCreds, nil } -func (s *sqlDatabase) sqlToCommonGithubEndpoint(ep GithubEndpoint) (params.GithubEndpoint, error) { - return params.GithubEndpoint{ +func (s *sqlDatabase) sqlToCommonGithubEndpoint(ep GithubEndpoint) (params.ForgeEndpoint, error) { + return params.ForgeEndpoint{ Name: ep.Name, Description: ep.Description, APIBaseURL: ep.APIBaseURL, @@ -115,7 +115,7 @@ func getUIDFromContext(ctx context.Context) (uuid.UUID, error) { return asUUID, nil } -func (s *sqlDatabase) CreateGithubEndpoint(_ context.Context, param params.CreateGithubEndpointParams) (ghEndpoint params.GithubEndpoint, err error) { +func (s *sqlDatabase) CreateGithubEndpoint(_ context.Context, param params.CreateGithubEndpointParams) (ghEndpoint params.ForgeEndpoint, err error) { defer func() { if err == nil { s.sendNotify(common.GithubEndpointEntityType, common.CreateOperation, ghEndpoint) @@ -141,23 +141,23 @@ func (s *sqlDatabase) CreateGithubEndpoint(_ context.Context, param params.Creat return nil }) if err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "creating github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "creating github endpoint") } ghEndpoint, err = s.sqlToCommonGithubEndpoint(endpoint) if err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "converting github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "converting github endpoint") } return ghEndpoint, nil } -func (s *sqlDatabase) ListGithubEndpoints(_ context.Context) ([]params.GithubEndpoint, error) { +func (s *sqlDatabase) ListGithubEndpoints(_ context.Context) ([]params.ForgeEndpoint, error) { var endpoints []GithubEndpoint err := s.conn.Find(&endpoints).Error if err != nil { return nil, errors.Wrap(err, "fetching github endpoints") } - var ret []params.GithubEndpoint + var ret []params.ForgeEndpoint for _, ep := range endpoints { commonEp, err := s.sqlToCommonGithubEndpoint(ep) if err != nil { @@ -168,9 +168,9 @@ func (s *sqlDatabase) ListGithubEndpoints(_ context.Context) ([]params.GithubEnd return ret, nil } -func (s *sqlDatabase) UpdateGithubEndpoint(_ context.Context, name string, param params.UpdateGithubEndpointParams) (ghEndpoint params.GithubEndpoint, err error) { +func (s *sqlDatabase) UpdateGithubEndpoint(_ context.Context, name string, param params.UpdateGithubEndpointParams) (ghEndpoint params.ForgeEndpoint, err error) { if name == defaultGithubEndpoint { - return params.GithubEndpoint{}, errors.Wrap(runnerErrors.ErrBadRequest, "cannot update default github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(runnerErrors.ErrBadRequest, "cannot update default github endpoint") } defer func() { @@ -213,24 +213,24 @@ func (s *sqlDatabase) UpdateGithubEndpoint(_ context.Context, name string, param return nil }) if err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "updating github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "updating github endpoint") } ghEndpoint, err = s.sqlToCommonGithubEndpoint(endpoint) if err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "converting github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "converting github endpoint") } return ghEndpoint, nil } -func (s *sqlDatabase) GetGithubEndpoint(_ context.Context, name string) (params.GithubEndpoint, error) { +func (s *sqlDatabase) GetGithubEndpoint(_ context.Context, name string) (params.ForgeEndpoint, error) { var endpoint GithubEndpoint err := s.conn.Where("name = ?", name).First(&endpoint).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return params.GithubEndpoint{}, errors.Wrap(runnerErrors.ErrNotFound, "github endpoint not found") + return params.ForgeEndpoint{}, errors.Wrap(runnerErrors.ErrNotFound, "github endpoint not found") } - return params.GithubEndpoint{}, errors.Wrap(err, "fetching github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "fetching github endpoint") } return s.sqlToCommonGithubEndpoint(endpoint) @@ -243,7 +243,7 @@ func (s *sqlDatabase) DeleteGithubEndpoint(_ context.Context, name string) (err defer func() { if err == nil { - s.sendNotify(common.GithubEndpointEntityType, common.DeleteOperation, params.GithubEndpoint{Name: name}) + s.sendNotify(common.GithubEndpointEntityType, common.DeleteOperation, params.ForgeEndpoint{Name: name}) } }() err = s.conn.Transaction(func(tx *gorm.DB) error { @@ -329,9 +329,9 @@ func (s *sqlDatabase) CreateGithubCredentials(ctx context.Context, param params. var data []byte var err error switch param.AuthType { - case params.GithubAuthTypePAT: + case params.ForgeAuthTypePAT: data, err = s.marshalAndSeal(param.PAT) - case params.GithubAuthTypeApp: + case params.ForgeAuthTypeApp: data, err = s.marshalAndSeal(param.App) default: return errors.Wrap(runnerErrors.ErrBadRequest, "invalid auth type") @@ -495,7 +495,7 @@ func (s *sqlDatabase) UpdateGithubCredentials(ctx context.Context, id uint, para var data []byte var err error switch creds.AuthType { - case params.GithubAuthTypePAT: + case params.ForgeAuthTypePAT: if param.PAT != nil { data, err = s.marshalAndSeal(param.PAT) } @@ -503,7 +503,7 @@ func (s *sqlDatabase) UpdateGithubCredentials(ctx context.Context, id uint, para if param.App != nil { return errors.Wrap(runnerErrors.ErrBadRequest, "cannot update app credentials for PAT") } - case params.GithubAuthTypeApp: + case params.ForgeAuthTypeApp: if param.App != nil { data, err = s.marshalAndSeal(param.App) } diff --git a/database/sql/github_test.go b/database/sql/github_test.go index 9d53569a..e46d963d 100644 --- a/database/sql/github_test.go +++ b/database/sql/github_test.go @@ -266,7 +266,7 @@ func (s *GithubTestSuite) TestCreateCredentials() { Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test", }, @@ -290,7 +290,7 @@ func (s *GithubTestSuite) TestCreateCredentialsFailsOnDuplicateCredentials() { Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test", }, @@ -320,7 +320,7 @@ func (s *GithubTestSuite) TestNormalUsersCanOnlySeeTheirOwnCredentialsAdminCanSe Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test", }, @@ -376,7 +376,7 @@ func (s *GithubTestSuite) TestGetGithubCredentialsByNameReturnsOnlyCurrentUserCr Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test", }, @@ -421,7 +421,7 @@ func (s *GithubTestSuite) TestGetGithubCredentials() { Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test", }, @@ -451,7 +451,7 @@ func (s *GithubTestSuite) TestDeleteGithubCredentials() { Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test", }, @@ -478,7 +478,7 @@ func (s *GithubTestSuite) TestDeleteGithubCredentialsByNonAdminUser() { Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test-creds4", }, @@ -523,7 +523,7 @@ func (s *GithubTestSuite) TestDeleteCredentialsFailsIfReposOrgsOrEntitiesUseIt() Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test", }, @@ -581,7 +581,7 @@ func (s *GithubTestSuite) TestUpdateCredentials() { Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test", }, @@ -616,7 +616,7 @@ func (s *GithubTestSuite) TestUpdateGithubCredentialsFailIfWrongCredentialTypeIs Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test", }, @@ -643,7 +643,7 @@ func (s *GithubTestSuite) TestUpdateGithubCredentialsFailIfWrongCredentialTypeIs Name: "test-credsApp", Description: "test credsApp", Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypeApp, + AuthType: params.ForgeAuthTypeApp, App: params.GithubApp{ AppID: 1, InstallationID: 2, @@ -688,7 +688,7 @@ func (s *GithubTestSuite) TestUpdateCredentialsFailsIfCredentialsAreOwnedByNonAd Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test-creds5", }, @@ -717,7 +717,7 @@ func (s *GithubTestSuite) TestAdminUserCanUpdateAnyGithubCredentials() { Name: testCredsName, Description: testCredsDescription, Endpoint: defaultGithubEndpoint, - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "test-creds5", }, @@ -836,10 +836,10 @@ func TestCredentialsAndEndpointMigration(t *testing.T) { t.Fatalf("expected ghes-test to be associated with example.com endpoint, got %s", creds[1].Endpoint.Name) } - if creds[0].AuthType != params.GithubAuthTypePAT { + if creds[0].AuthType != params.ForgeAuthTypePAT { t.Fatalf("expected test-creds to have PAT auth type, got %s", creds[0].AuthType) } - if creds[1].AuthType != params.GithubAuthTypeApp { + if creds[1].AuthType != params.ForgeAuthTypeApp { t.Fatalf("expected ghes-test to have App auth type, got %s", creds[1].AuthType) } if len(creds[0].CredentialsPayload) == 0 { diff --git a/database/sql/jobs.go b/database/sql/jobs.go index b7dda926..7f9b7b00 100644 --- a/database/sql/jobs.go +++ b/database/sql/jobs.go @@ -306,7 +306,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.GithubEntityType, entityID string, status params.JobStatus) ([]params.Job, error) { +func (s *sqlDatabase) ListEntityJobsByStatus(_ context.Context, entityType params.ForgeEntityType, entityID string, status params.JobStatus) ([]params.Job, error) { u, err := uuid.Parse(entityID) if err != nil { return nil, err @@ -316,11 +316,11 @@ func (s *sqlDatabase) ListEntityJobsByStatus(_ context.Context, entityType param query := s.conn.Model(&WorkflowJob{}).Preload("Instance").Where("status = ?", status) switch entityType { - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: query = query.Where("org_id = ?", u) - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: query = query.Where("repo_id = ?", u) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: query = query.Where("enterprise_id = ?", u) } diff --git a/database/sql/models.go b/database/sql/models.go index 2a04db0c..2accccc4 100644 --- a/database/sql/models.go +++ b/database/sql/models.go @@ -397,9 +397,9 @@ type GithubCredentials struct { 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"` + Description string `gorm:"type:text"` + AuthType params.ForgeAuthType `gorm:"index"` + Payload []byte `gorm:"type:longblob"` Endpoint GithubEndpoint `gorm:"foreignKey:EndpointName"` EndpointName *string `gorm:"index"` diff --git a/database/sql/organizations_test.go b/database/sql/organizations_test.go index b30ea701..030a3abe 100644 --- a/database/sql/organizations_test.go +++ b/database/sql/organizations_test.go @@ -55,7 +55,7 @@ type OrgTestSuite struct { testCreds params.GithubCredentials secondaryTestCreds params.GithubCredentials - githubEndpoint params.GithubEndpoint + githubEndpoint params.ForgeEndpoint } func (s *OrgTestSuite) equalInstancesByName(expected, actual []params.Instance) { @@ -492,9 +492,9 @@ func (s *OrgTestSuite) TestCreateOrganizationPoolMissingTags() { } func (s *OrgTestSuite) TestCreateOrganizationPoolInvalidOrgID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-org-id", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } _, err := s.Store.CreateEntityPool(s.adminCtx, entity, s.Fixtures.CreatePoolParams) @@ -640,9 +640,9 @@ func (s *OrgTestSuite) TestListOrgPools() { } func (s *OrgTestSuite) TestListOrgPoolsInvalidOrgID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-org-id", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } _, err := s.Store.ListEntityPools(s.adminCtx, entity) @@ -665,9 +665,9 @@ func (s *OrgTestSuite) TestGetOrganizationPool() { } func (s *OrgTestSuite) TestGetOrganizationPoolInvalidOrgID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-org-id", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } _, err := s.Store.GetEntityPool(s.adminCtx, entity, "dummy-pool-id") @@ -691,9 +691,9 @@ func (s *OrgTestSuite) TestDeleteOrganizationPool() { } func (s *OrgTestSuite) TestDeleteOrganizationPoolInvalidOrgID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-org-id", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } err := s.Store.DeleteEntityPool(s.adminCtx, entity, "dummy-pool-id") @@ -748,9 +748,9 @@ func (s *OrgTestSuite) TestListOrgInstances() { } func (s *OrgTestSuite) TestListOrgInstancesInvalidOrgID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-org-id", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } _, err := s.Store.ListEntityInstances(s.adminCtx, entity) @@ -776,9 +776,9 @@ func (s *OrgTestSuite) TestUpdateOrganizationPool() { } func (s *OrgTestSuite) TestUpdateOrganizationPoolInvalidOrgID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-org-id", - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } _, err := s.Store.UpdateEntityPool(s.adminCtx, entity, "dummy-pool-id", s.Fixtures.UpdatePoolParams) diff --git a/database/sql/pools.go b/database/sql/pools.go index 5cb6d136..24476fe8 100644 --- a/database/sql/pools.go +++ b/database/sql/pools.go @@ -86,7 +86,7 @@ func (s *sqlDatabase) DeletePoolByID(_ context.Context, poolID string) (err erro return nil } -func (s *sqlDatabase) getEntityPool(tx *gorm.DB, entityType params.GithubEntityType, entityID, poolID string, preload ...string) (Pool, error) { +func (s *sqlDatabase) getEntityPool(tx *gorm.DB, entityType params.ForgeEntityType, entityID, poolID string, preload ...string) (Pool, error) { if entityID == "" { return Pool{}, errors.Wrap(runnerErrors.ErrBadRequest, "missing entity id") } @@ -99,13 +99,13 @@ func (s *sqlDatabase) getEntityPool(tx *gorm.DB, entityType params.GithubEntityT var fieldName string var entityField string switch entityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: fieldName = entityTypeRepoName entityField = repositoryFieldName - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: fieldName = entityTypeOrgName entityField = organizationFieldName - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: fieldName = entityTypeEnterpriseName entityField = enterpriseFieldName default: @@ -135,7 +135,7 @@ func (s *sqlDatabase) getEntityPool(tx *gorm.DB, entityType params.GithubEntityT return pool, nil } -func (s *sqlDatabase) listEntityPools(tx *gorm.DB, entityType params.GithubEntityType, entityID string, preload ...string) ([]Pool, error) { +func (s *sqlDatabase) listEntityPools(tx *gorm.DB, entityType params.ForgeEntityType, entityID string, preload ...string) ([]Pool, error) { if _, err := uuid.Parse(entityID); err != nil { return nil, errors.Wrap(runnerErrors.ErrBadRequest, "parsing id") } @@ -147,13 +147,13 @@ func (s *sqlDatabase) listEntityPools(tx *gorm.DB, entityType params.GithubEntit var preloadEntity string var fieldName string switch entityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: fieldName = entityTypeRepoName preloadEntity = "Repository" - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: fieldName = entityTypeOrgName preloadEntity = "Organization" - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: fieldName = entityTypeEnterpriseName preloadEntity = "Enterprise" default: @@ -184,7 +184,7 @@ func (s *sqlDatabase) listEntityPools(tx *gorm.DB, entityType params.GithubEntit return pools, nil } -func (s *sqlDatabase) findPoolByTags(id string, poolType params.GithubEntityType, tags []string) ([]params.Pool, error) { +func (s *sqlDatabase) findPoolByTags(id string, poolType params.ForgeEntityType, tags []string) ([]params.Pool, error) { if len(tags) == 0 { return nil, runnerErrors.NewBadRequestError("missing tags") } @@ -195,11 +195,11 @@ func (s *sqlDatabase) findPoolByTags(id string, poolType params.GithubEntityType var fieldName string switch poolType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: fieldName = entityTypeRepoName - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: fieldName = entityTypeOrgName - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: fieldName = entityTypeEnterpriseName default: return nil, fmt.Errorf("invalid poolType: %v", poolType) @@ -238,7 +238,7 @@ func (s *sqlDatabase) findPoolByTags(id string, poolType params.GithubEntityType return ret, nil } -func (s *sqlDatabase) FindPoolsMatchingAllTags(_ context.Context, entityType params.GithubEntityType, entityID string, tags []string) ([]params.Pool, error) { +func (s *sqlDatabase) FindPoolsMatchingAllTags(_ context.Context, entityType params.ForgeEntityType, entityID string, tags []string) ([]params.Pool, error) { if len(tags) == 0 { return nil, runnerErrors.NewBadRequestError("missing tags") } @@ -254,7 +254,7 @@ func (s *sqlDatabase) FindPoolsMatchingAllTags(_ context.Context, entityType par return pools, nil } -func (s *sqlDatabase) CreateEntityPool(_ context.Context, entity params.GithubEntity, param params.CreatePoolParams) (pool params.Pool, err error) { +func (s *sqlDatabase) CreateEntityPool(_ context.Context, entity params.ForgeEntity, param params.CreatePoolParams) (pool params.Pool, err error) { if len(param.Tags) == 0 { return params.Pool{}, runnerErrors.NewBadRequestError("no tags specified") } @@ -289,11 +289,11 @@ func (s *sqlDatabase) CreateEntityPool(_ context.Context, entity params.GithubEn } switch entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: newPool.RepoID = &entityID - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: newPool.OrgID = &entityID - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: newPool.EnterpriseID = &entityID } err = s.conn.Transaction(func(tx *gorm.DB) error { @@ -334,7 +334,7 @@ func (s *sqlDatabase) CreateEntityPool(_ context.Context, entity params.GithubEn return s.sqlToCommonPool(dbPool) } -func (s *sqlDatabase) GetEntityPool(_ context.Context, entity params.GithubEntity, poolID string) (params.Pool, error) { +func (s *sqlDatabase) GetEntityPool(_ context.Context, entity params.ForgeEntity, poolID string) (params.Pool, error) { pool, err := s.getEntityPool(s.conn, entity.EntityType, entity.ID, poolID, "Tags", "Instances") if err != nil { return params.Pool{}, fmt.Errorf("fetching pool: %w", err) @@ -342,7 +342,7 @@ func (s *sqlDatabase) GetEntityPool(_ context.Context, entity params.GithubEntit return s.sqlToCommonPool(pool) } -func (s *sqlDatabase) DeleteEntityPool(_ context.Context, entity params.GithubEntity, poolID string) (err error) { +func (s *sqlDatabase) DeleteEntityPool(_ context.Context, entity params.ForgeEntity, poolID string) (err error) { entityID, err := uuid.Parse(entity.ID) if err != nil { return errors.Wrap(runnerErrors.ErrBadRequest, "parsing id") @@ -363,11 +363,11 @@ func (s *sqlDatabase) DeleteEntityPool(_ context.Context, entity params.GithubEn } var fieldName string switch entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: fieldName = entityTypeRepoName - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: fieldName = entityTypeOrgName - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: fieldName = entityTypeEnterpriseName default: return fmt.Errorf("invalid entityType: %v", entity.EntityType) @@ -379,7 +379,7 @@ func (s *sqlDatabase) DeleteEntityPool(_ context.Context, entity params.GithubEn return nil } -func (s *sqlDatabase) UpdateEntityPool(_ context.Context, entity params.GithubEntity, poolID string, param params.UpdatePoolParams) (updatedPool params.Pool, err error) { +func (s *sqlDatabase) UpdateEntityPool(_ context.Context, entity params.ForgeEntity, poolID string, param params.UpdatePoolParams) (updatedPool params.Pool, err error) { defer func() { if err == nil { s.sendNotify(common.PoolEntityType, common.UpdateOperation, updatedPool) @@ -403,7 +403,7 @@ func (s *sqlDatabase) UpdateEntityPool(_ context.Context, entity params.GithubEn return updatedPool, nil } -func (s *sqlDatabase) ListEntityPools(_ context.Context, entity params.GithubEntity) ([]params.Pool, error) { +func (s *sqlDatabase) ListEntityPools(_ context.Context, entity params.ForgeEntity) ([]params.Pool, error) { pools, err := s.listEntityPools(s.conn, entity.EntityType, entity.ID, "Tags") if err != nil { return nil, errors.Wrap(err, "fetching pools") @@ -420,7 +420,7 @@ func (s *sqlDatabase) ListEntityPools(_ context.Context, entity params.GithubEnt return ret, nil } -func (s *sqlDatabase) ListEntityInstances(_ context.Context, entity params.GithubEntity) ([]params.Instance, error) { +func (s *sqlDatabase) ListEntityInstances(_ context.Context, entity params.ForgeEntity) ([]params.Instance, error) { pools, err := s.listEntityPools(s.conn, entity.EntityType, entity.ID, "Instances", "Instances.Job") if err != nil { return nil, errors.Wrap(err, "fetching entity") diff --git a/database/sql/repositories_test.go b/database/sql/repositories_test.go index 484742ae..f43b9357 100644 --- a/database/sql/repositories_test.go +++ b/database/sql/repositories_test.go @@ -60,7 +60,7 @@ type RepoTestSuite struct { testCreds params.GithubCredentials secondaryTestCreds params.GithubCredentials - githubEndpoint params.GithubEndpoint + githubEndpoint params.ForgeEndpoint } func (s *RepoTestSuite) equalReposByName(expected, actual []params.Repository) { @@ -541,9 +541,9 @@ func (s *RepoTestSuite) TestCreateRepositoryPoolMissingTags() { } func (s *RepoTestSuite) TestCreateRepositoryPoolInvalidRepoID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-repo-id", - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } _, err := s.Store.CreateEntityPool(s.adminCtx, entity, s.Fixtures.CreatePoolParams) @@ -692,9 +692,9 @@ func (s *RepoTestSuite) TestListRepoPools() { } func (s *RepoTestSuite) TestListRepoPoolsInvalidRepoID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-repo-id", - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } _, err := s.Store.ListEntityPools(s.adminCtx, entity) @@ -717,9 +717,9 @@ func (s *RepoTestSuite) TestGetRepositoryPool() { } func (s *RepoTestSuite) TestGetRepositoryPoolInvalidRepoID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-repo-id", - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } _, err := s.Store.GetEntityPool(s.adminCtx, entity, "dummy-pool-id") @@ -743,9 +743,9 @@ func (s *RepoTestSuite) TestDeleteRepositoryPool() { } func (s *RepoTestSuite) TestDeleteRepositoryPoolInvalidRepoID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-repo-id", - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } err := s.Store.DeleteEntityPool(s.adminCtx, entity, "dummy-pool-id") @@ -799,9 +799,9 @@ func (s *RepoTestSuite) TestListRepoInstances() { } func (s *RepoTestSuite) TestListRepoInstancesInvalidRepoID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-repo-id", - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } _, err := s.Store.ListEntityInstances(s.adminCtx, entity) @@ -827,9 +827,9 @@ func (s *RepoTestSuite) TestUpdateRepositoryPool() { } func (s *RepoTestSuite) TestUpdateRepositoryPoolInvalidRepoID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: "dummy-repo-id", - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } _, err := s.Store.UpdateEntityPool(s.adminCtx, entity, "dummy-repo-id", s.Fixtures.UpdatePoolParams) diff --git a/database/sql/scalesets.go b/database/sql/scalesets.go index e4bd28f3..1d272704 100644 --- a/database/sql/scalesets.go +++ b/database/sql/scalesets.go @@ -53,7 +53,7 @@ func (s *sqlDatabase) ListAllScaleSets(_ context.Context) ([]params.ScaleSet, er return ret, nil } -func (s *sqlDatabase) CreateEntityScaleSet(_ context.Context, entity params.GithubEntity, param params.CreateScaleSetParams) (scaleSet params.ScaleSet, err error) { +func (s *sqlDatabase) CreateEntityScaleSet(_ context.Context, entity params.ForgeEntity, param params.CreateScaleSetParams) (scaleSet params.ScaleSet, err error) { if err := param.Validate(); err != nil { return params.ScaleSet{}, fmt.Errorf("failed to validate create params: %w", err) } @@ -92,11 +92,11 @@ func (s *sqlDatabase) CreateEntityScaleSet(_ context.Context, entity params.Gith } switch entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: newScaleSet.RepoID = &entityID - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: newScaleSet.OrgID = &entityID - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: newScaleSet.EnterpriseID = &entityID } err = s.conn.Transaction(func(tx *gorm.DB) error { @@ -123,7 +123,7 @@ func (s *sqlDatabase) CreateEntityScaleSet(_ context.Context, entity params.Gith return s.sqlToCommonScaleSet(dbScaleSet) } -func (s *sqlDatabase) listEntityScaleSets(tx *gorm.DB, entityType params.GithubEntityType, entityID string, preload ...string) ([]ScaleSet, error) { +func (s *sqlDatabase) listEntityScaleSets(tx *gorm.DB, entityType params.ForgeEntityType, entityID string, preload ...string) ([]ScaleSet, error) { if _, err := uuid.Parse(entityID); err != nil { return nil, errors.Wrap(runnerErrors.ErrBadRequest, "parsing id") } @@ -135,13 +135,13 @@ func (s *sqlDatabase) listEntityScaleSets(tx *gorm.DB, entityType params.GithubE var preloadEntity string var fieldName string switch entityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: fieldName = entityTypeRepoName preloadEntity = repositoryFieldName - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: fieldName = entityTypeOrgName preloadEntity = organizationFieldName - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: fieldName = entityTypeEnterpriseName preloadEntity = enterpriseFieldName default: @@ -173,7 +173,7 @@ func (s *sqlDatabase) listEntityScaleSets(tx *gorm.DB, entityType params.GithubE return scaleSets, nil } -func (s *sqlDatabase) ListEntityScaleSets(_ context.Context, entity params.GithubEntity) ([]params.ScaleSet, error) { +func (s *sqlDatabase) ListEntityScaleSets(_ context.Context, entity params.ForgeEntity) ([]params.ScaleSet, error) { scaleSets, err := s.listEntityScaleSets(s.conn, entity.EntityType, entity.ID) if err != nil { return nil, errors.Wrap(err, "fetching scale sets") @@ -190,7 +190,7 @@ func (s *sqlDatabase) ListEntityScaleSets(_ context.Context, entity params.Githu return ret, nil } -func (s *sqlDatabase) UpdateEntityScaleSet(_ context.Context, entity params.GithubEntity, scaleSetID uint, param params.UpdateScaleSetParams, callback func(old, newSet params.ScaleSet) error) (updatedScaleSet params.ScaleSet, err error) { +func (s *sqlDatabase) UpdateEntityScaleSet(_ context.Context, entity params.ForgeEntity, scaleSetID uint, param params.UpdateScaleSetParams, callback func(old, newSet params.ScaleSet) error) (updatedScaleSet params.ScaleSet, err error) { defer func() { if err == nil { s.sendNotify(common.ScaleSetEntityType, common.UpdateOperation, updatedScaleSet) @@ -225,7 +225,7 @@ func (s *sqlDatabase) UpdateEntityScaleSet(_ context.Context, entity params.Gith return updatedScaleSet, nil } -func (s *sqlDatabase) getEntityScaleSet(tx *gorm.DB, entityType params.GithubEntityType, entityID string, scaleSetID uint, preload ...string) (ScaleSet, error) { +func (s *sqlDatabase) getEntityScaleSet(tx *gorm.DB, entityType params.ForgeEntityType, entityID string, scaleSetID uint, preload ...string) (ScaleSet, error) { if entityID == "" { return ScaleSet{}, errors.Wrap(runnerErrors.ErrBadRequest, "missing entity id") } @@ -237,13 +237,13 @@ func (s *sqlDatabase) getEntityScaleSet(tx *gorm.DB, entityType params.GithubEnt var fieldName string var entityField string switch entityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: fieldName = entityTypeRepoName entityField = "Repository" - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: fieldName = entityTypeOrgName entityField = "Organization" - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: fieldName = entityTypeEnterpriseName entityField = "Enterprise" default: diff --git a/database/sql/scalesets_test.go b/database/sql/scalesets_test.go index 951c3735..9b8b241d 100644 --- a/database/sql/scalesets_test.go +++ b/database/sql/scalesets_test.go @@ -25,9 +25,9 @@ type ScaleSetsTestSuite struct { repo params.Repository enterprise params.Enterprise - orgEntity params.GithubEntity - repoEntity params.GithubEntity - enterpriseEntity params.GithubEntity + orgEntity params.ForgeEntity + repoEntity params.ForgeEntity + enterpriseEntity params.ForgeEntity } func (s *ScaleSetsTestSuite) SetupTest() { @@ -298,7 +298,7 @@ func (s *ScaleSetsTestSuite) TestScaleSetOperations() { }) s.T().Run("update scaleset with invalid entity", func(_ *testing.T) { - _, err = s.Store.UpdateEntityScaleSet(s.adminCtx, params.GithubEntity{}, enterpriseScaleSet.ID, params.UpdateScaleSetParams{}, nil) + _, err = s.Store.UpdateEntityScaleSet(s.adminCtx, params.ForgeEntity{}, enterpriseScaleSet.ID, params.UpdateScaleSetParams{}, nil) s.Require().Error(err) s.Require().Contains(err.Error(), "missing entity id") }) diff --git a/database/sql/sql.go b/database/sql/sql.go index 86729ad6..82601316 100644 --- a/database/sql/sql.go +++ b/database/sql/sql.go @@ -299,7 +299,7 @@ func (s *sqlDatabase) migrateCredentialsToDB() (err error) { CACertBundle: certBundle, } - var endpoint params.GithubEndpoint + var endpoint params.ForgeEndpoint endpoint, err = s.GetGithubEndpoint(adminCtx, hostname) if err != nil { if !errors.Is(err, runnerErrors.ErrNotFound) { @@ -315,10 +315,10 @@ func (s *sqlDatabase) migrateCredentialsToDB() (err error) { Name: cred.Name, Description: cred.Description, Endpoint: endpoint.Name, - AuthType: params.GithubAuthType(cred.GetAuthType()), + AuthType: params.ForgeAuthType(cred.GetAuthType()), } switch credParams.AuthType { - case params.GithubAuthTypeApp: + case params.ForgeAuthTypeApp: keyBytes, err := cred.App.PrivateKeyBytes() if err != nil { return errors.Wrap(err, "getting private key bytes") @@ -332,7 +332,7 @@ func (s *sqlDatabase) migrateCredentialsToDB() (err error) { if err := credParams.App.Validate(); err != nil { return errors.Wrap(err, "validating app credentials") } - case params.GithubAuthTypePAT: + case params.ForgeAuthTypePAT: token := cred.PAT.OAuth2Token if token == "" { token = cred.OAuth2Token diff --git a/database/sql/util.go b/database/sql/util.go index 62f22179..a2531449 100644 --- a/database/sql/util.go +++ b/database/sql/util.go @@ -546,18 +546,18 @@ func (s *sqlDatabase) getScaleSetByID(tx *gorm.DB, scaleSetID uint, preload ...s return scaleSet, nil } -func (s *sqlDatabase) hasGithubEntity(tx *gorm.DB, entityType params.GithubEntityType, entityID string) error { +func (s *sqlDatabase) hasGithubEntity(tx *gorm.DB, entityType params.ForgeEntityType, entityID string) error { u, err := uuid.Parse(entityID) if err != nil { return errors.Wrap(runnerErrors.ErrBadRequest, "parsing id") } var q *gorm.DB switch entityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: q = tx.Model(&Repository{}).Where("id = ?", u) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: q = tx.Model(&Organization{}).Where("id = ?", u) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: q = tx.Model(&Enterprise{}).Where("id = ?", u) default: return errors.Wrap(runnerErrors.ErrBadRequest, "invalid entity type") @@ -608,26 +608,26 @@ func (s *sqlDatabase) sendNotify(entityType dbCommon.DatabaseEntityType, op dbCo return s.producer.Notify(message) } -func (s *sqlDatabase) GetGithubEntity(_ context.Context, entityType params.GithubEntityType, entityID string) (params.GithubEntity, error) { +func (s *sqlDatabase) GetForgeEntity(_ context.Context, entityType params.ForgeEntityType, entityID string) (params.ForgeEntity, error) { var ghEntity params.EntityGetter var err error switch entityType { - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: ghEntity, err = s.GetEnterpriseByID(s.ctx, entityID) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ghEntity, err = s.GetOrganizationByID(s.ctx, entityID) - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ghEntity, err = s.GetRepositoryByID(s.ctx, entityID) default: - return params.GithubEntity{}, errors.Wrap(runnerErrors.ErrBadRequest, "invalid entity type") + return params.ForgeEntity{}, errors.Wrap(runnerErrors.ErrBadRequest, "invalid entity type") } if err != nil { - return params.GithubEntity{}, errors.Wrap(err, "failed to get ") + return params.ForgeEntity{}, errors.Wrap(err, "failed to get ") } entity, err := ghEntity.GetEntity() if err != nil { - return params.GithubEntity{}, errors.Wrap(err, "failed to get entity") + return params.ForgeEntity{}, errors.Wrap(err, "failed to get entity") } return entity, nil } @@ -747,17 +747,17 @@ func (s *sqlDatabase) addEnterpriseEvent(ctx context.Context, entID string, even return nil } -func (s *sqlDatabase) AddEntityEvent(ctx context.Context, entity params.GithubEntity, event params.EventType, eventLevel params.EventLevel, statusMessage string, maxEvents int) error { +func (s *sqlDatabase) AddEntityEvent(ctx context.Context, entity params.ForgeEntity, event params.EventType, eventLevel params.EventLevel, statusMessage string, maxEvents int) error { if maxEvents == 0 { return errors.Wrap(runnerErrors.ErrBadRequest, "max events cannot be 0") } switch entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: return s.addRepositoryEvent(ctx, entity.ID, event, eventLevel, statusMessage, maxEvents) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: return s.addOrgEvent(ctx, entity.ID, event, eventLevel, statusMessage, maxEvents) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: return s.addEnterpriseEvent(ctx, entity.ID, event, eventLevel, statusMessage, maxEvents) default: return errors.Wrap(runnerErrors.ErrBadRequest, "invalid entity type") diff --git a/database/watcher/filters.go b/database/watcher/filters.go index dfcd54bb..51820270 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -63,7 +63,7 @@ func WithOperationTypeFilter(operationType dbCommon.OperationType) dbCommon.Payl // WithEntityPoolFilter returns true if the change payload is a pool that belongs to the // supplied Github entity. This is useful when an entity worker wants to watch for changes // in pools that belong to it. -func WithEntityPoolFilter(ghEntity params.GithubEntity) dbCommon.PayloadFilterFunc { +func WithEntityPoolFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { switch payload.EntityType { case dbCommon.PoolEntityType: @@ -72,11 +72,11 @@ func WithEntityPoolFilter(ghEntity params.GithubEntity) dbCommon.PayloadFilterFu return false } switch ghEntity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: return pool.RepoID == ghEntity.ID - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: return pool.OrgID == ghEntity.ID - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: return pool.EnterpriseID == ghEntity.ID default: return false @@ -90,7 +90,7 @@ func WithEntityPoolFilter(ghEntity params.GithubEntity) dbCommon.PayloadFilterFu // WithEntityPoolFilter returns true if the change payload is a pool that belongs to the // supplied Github entity. This is useful when an entity worker wants to watch for changes // in pools that belong to it. -func WithEntityScaleSetFilter(ghEntity params.GithubEntity) dbCommon.PayloadFilterFunc { +func WithEntityScaleSetFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { switch payload.EntityType { case dbCommon.ScaleSetEntityType: @@ -99,11 +99,11 @@ func WithEntityScaleSetFilter(ghEntity params.GithubEntity) dbCommon.PayloadFilt return false } switch ghEntity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: return scaleSet.RepoID == ghEntity.ID - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: return scaleSet.OrgID == ghEntity.ID - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: return scaleSet.EnterpriseID == ghEntity.ID default: return false @@ -116,26 +116,26 @@ func WithEntityScaleSetFilter(ghEntity params.GithubEntity) dbCommon.PayloadFilt // WithEntityFilter returns a filter function that filters payloads by entity. // Change payloads that match the entity type and ID will return true. -func WithEntityFilter(entity params.GithubEntity) dbCommon.PayloadFilterFunc { +func WithEntityFilter(entity params.ForgeEntity) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { - if params.GithubEntityType(payload.EntityType) != entity.EntityType { + if params.ForgeEntityType(payload.EntityType) != entity.EntityType { return false } var ent IDGetter var ok bool switch payload.EntityType { case dbCommon.RepositoryEntityType: - if entity.EntityType != params.GithubEntityTypeRepository { + if entity.EntityType != params.ForgeEntityTypeRepository { return false } ent, ok = payload.Payload.(params.Repository) case dbCommon.OrganizationEntityType: - if entity.EntityType != params.GithubEntityTypeOrganization { + if entity.EntityType != params.ForgeEntityTypeOrganization { return false } ent, ok = payload.Payload.(params.Organization) case dbCommon.EnterpriseEntityType: - if entity.EntityType != params.GithubEntityTypeEnterprise { + if entity.EntityType != params.ForgeEntityTypeEnterprise { return false } ent, ok = payload.Payload.(params.Enterprise) @@ -149,7 +149,7 @@ func WithEntityFilter(entity params.GithubEntity) dbCommon.PayloadFilterFunc { } } -func WithEntityJobFilter(ghEntity params.GithubEntity) dbCommon.PayloadFilterFunc { +func WithEntityJobFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { switch payload.EntityType { case dbCommon.JobEntityType: @@ -159,15 +159,15 @@ func WithEntityJobFilter(ghEntity params.GithubEntity) dbCommon.PayloadFilterFun } switch ghEntity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: if job.RepoID != nil && job.RepoID.String() != ghEntity.ID { return false } - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: if job.OrgID != nil && job.OrgID.String() != ghEntity.ID { return false } - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: if job.EnterpriseID != nil && job.EnterpriseID.String() != ghEntity.ID { return false } @@ -183,16 +183,20 @@ func WithEntityJobFilter(ghEntity params.GithubEntity) dbCommon.PayloadFilterFun } // WithGithubCredentialsFilter returns a filter function that filters payloads by Github credentials. -func WithGithubCredentialsFilter(creds params.GithubCredentials) dbCommon.PayloadFilterFunc { +func WithForgeCredentialsFilter(creds params.ForgeCredentials) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { - if payload.EntityType != dbCommon.GithubCredentialsEntityType { + var idGetter params.IDGetter + var ok bool + switch payload.EntityType { + case dbCommon.GithubCredentialsEntityType: + idGetter, ok = payload.Payload.(params.GithubCredentials) + default: return false } - credsPayload, ok := payload.Payload.(params.GithubCredentials) if !ok { return false } - return credsPayload.ID == creds.ID + return idGetter.GetID() == creds.GetID() } } diff --git a/database/watcher/watcher_store_test.go b/database/watcher/watcher_store_test.go index 5a1486a8..2300ac0a 100644 --- a/database/watcher/watcher_store_test.go +++ b/database/watcher/watcher_store_test.go @@ -848,7 +848,7 @@ func (s *WatcherStoreTestSuite) TestGithubCredentialsWatcher() { Name: "test-creds", Description: "test credentials", Endpoint: "github.com", - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "bogus", }, @@ -971,7 +971,7 @@ func (s *WatcherStoreTestSuite) TestGithubEndpointWatcher() { EntityType: common.GithubEndpointEntityType, Operation: common.DeleteOperation, // We only get the name of the deleted entity - Payload: params.GithubEndpoint{Name: ghEp.Name}, + Payload: params.ForgeEndpoint{Name: ghEp.Name}, }, event) case <-time.After(1 * time.Second): s.T().Fatal("expected payload not received") diff --git a/internal/testing/testing.go b/internal/testing/testing.go index b3d049fd..6f253267 100644 --- a/internal/testing/testing.go +++ b/internal/testing/testing.go @@ -85,7 +85,7 @@ func CreateGARMTestUser(ctx context.Context, username string, db common.Store, s return user } -func CreateDefaultGithubEndpoint(ctx context.Context, db common.Store, s *testing.T) params.GithubEndpoint { +func CreateDefaultGithubEndpoint(ctx context.Context, db common.Store, s *testing.T) params.ForgeEndpoint { endpointParams := params.CreateGithubEndpointParams{ Name: "github.com", Description: "github endpoint", @@ -110,11 +110,11 @@ func CreateDefaultGithubEndpoint(ctx context.Context, db common.Store, s *testin return ep } -func CreateTestGithubCredentials(ctx context.Context, credsName string, db common.Store, s *testing.T, endpoint params.GithubEndpoint) params.GithubCredentials { +func CreateTestGithubCredentials(ctx context.Context, credsName string, db common.Store, s *testing.T, endpoint params.ForgeEndpoint) params.GithubCredentials { newCredsParams := params.CreateGithubCredentialsParams{ Name: credsName, Description: "Test creds", - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, Endpoint: endpoint.Name, PAT: params.GithubPAT{ OAuth2Token: "test-token", diff --git a/params/interfaces.go b/params/interfaces.go index cd9b94ff..ccf04e06 100644 --- a/params/interfaces.go +++ b/params/interfaces.go @@ -5,7 +5,7 @@ import "time" // EntityGetter is implemented by all github entities (repositories, organizations and enterprises). // It defines the GetEntity() function which returns a github entity. type EntityGetter interface { - GetEntity() (GithubEntity, error) + GetEntity() (ForgeEntity, error) } type IDGetter interface { @@ -15,3 +15,7 @@ type IDGetter interface { type CreationDateGetter interface { GetCreatedAt() time.Time } + +type ForgeCredentialsGetter interface { + GetForgeCredentials() ForgeCredentials +} diff --git a/params/params.go b/params/params.go index fdf3b836..daa54b61 100644 --- a/params/params.go +++ b/params/params.go @@ -36,14 +36,14 @@ import ( ) type ( - GithubEntityType string + ForgeEntityType string EventType string EventLevel string ProviderType string JobStatus string RunnerStatus string WebhookEndpointType string - GithubAuthType string + ForgeAuthType string EndpointType string PoolBalancerType string ScaleSetState string @@ -106,9 +106,9 @@ const ( ) const ( - GithubEntityTypeRepository GithubEntityType = "repository" - GithubEntityTypeOrganization GithubEntityType = "organization" - GithubEntityTypeEnterprise GithubEntityType = "enterprise" + ForgeEntityTypeRepository ForgeEntityType = "repository" + ForgeEntityTypeOrganization ForgeEntityType = "organization" + ForgeEntityTypeEnterprise ForgeEntityType = "enterprise" ) const ( @@ -141,13 +141,13 @@ const ( ) const ( - // GithubAuthTypePAT is the OAuth token based authentication - GithubAuthTypePAT GithubAuthType = "pat" - // GithubAuthTypeApp is the GitHub App based authentication - GithubAuthTypeApp GithubAuthType = "app" + // ForgeAuthTypePAT is the OAuth token based authentication + ForgeAuthTypePAT ForgeAuthType = "pat" + // ForgeAuthTypeApp is the GitHub App based authentication + ForgeAuthTypeApp ForgeAuthType = "app" ) -func (e GithubEntityType) String() string { +func (e ForgeEntityType) String() string { return string(e) } @@ -380,13 +380,13 @@ type Pool struct { Priority uint `json:"priority,omitempty"` } -func (p Pool) BelongsTo(entity GithubEntity) bool { +func (p Pool) BelongsTo(entity ForgeEntity) bool { switch p.PoolType() { - case GithubEntityTypeRepository: + case ForgeEntityTypeRepository: return p.RepoID == entity.ID - case GithubEntityTypeOrganization: + case ForgeEntityTypeOrganization: return p.OrgID == entity.ID - case GithubEntityTypeEnterprise: + case ForgeEntityTypeEnterprise: return p.EnterpriseID == entity.ID } return false @@ -411,25 +411,25 @@ func (p Pool) MaxRunnersAsInt() int { return int(p.MaxRunners) } -func (p Pool) GetEntity() (GithubEntity, error) { +func (p Pool) GetEntity() (ForgeEntity, error) { switch p.PoolType() { - case GithubEntityTypeRepository: - return GithubEntity{ + case ForgeEntityTypeRepository: + return ForgeEntity{ ID: p.RepoID, - EntityType: GithubEntityTypeRepository, + EntityType: ForgeEntityTypeRepository, }, nil - case GithubEntityTypeOrganization: - return GithubEntity{ + case ForgeEntityTypeOrganization: + return ForgeEntity{ ID: p.OrgID, - EntityType: GithubEntityTypeOrganization, + EntityType: ForgeEntityTypeOrganization, }, nil - case GithubEntityTypeEnterprise: - return GithubEntity{ + case ForgeEntityTypeEnterprise: + return ForgeEntity{ ID: p.EnterpriseID, - EntityType: GithubEntityTypeEnterprise, + EntityType: ForgeEntityTypeEnterprise, }, nil } - return GithubEntity{}, fmt.Errorf("pool has no associated entity") + return ForgeEntity{}, fmt.Errorf("pool has no associated entity") } func (p Pool) GetID() string { @@ -443,14 +443,14 @@ func (p *Pool) RunnerTimeout() uint { return p.RunnerBootstrapTimeout } -func (p *Pool) PoolType() GithubEntityType { +func (p *Pool) PoolType() ForgeEntityType { switch { case p.RepoID != "": - return GithubEntityTypeRepository + return ForgeEntityTypeRepository case p.OrgID != "": - return GithubEntityTypeOrganization + return ForgeEntityTypeOrganization case p.EnterpriseID != "": - return GithubEntityTypeEnterprise + return ForgeEntityTypeEnterprise } return "" } @@ -519,13 +519,13 @@ type ScaleSet struct { LastMessageID int64 `json:"-"` } -func (p ScaleSet) BelongsTo(entity GithubEntity) bool { +func (p ScaleSet) BelongsTo(entity ForgeEntity) bool { switch p.ScaleSetType() { - case GithubEntityTypeRepository: + case ForgeEntityTypeRepository: return p.RepoID == entity.ID - case GithubEntityTypeOrganization: + case ForgeEntityTypeOrganization: return p.OrgID == entity.ID - case GithubEntityTypeEnterprise: + case ForgeEntityTypeEnterprise: return p.EnterpriseID == entity.ID } return false @@ -535,35 +535,35 @@ func (p ScaleSet) GetID() uint { return p.ID } -func (p ScaleSet) GetEntity() (GithubEntity, error) { +func (p ScaleSet) GetEntity() (ForgeEntity, error) { switch p.ScaleSetType() { - case GithubEntityTypeRepository: - return GithubEntity{ + case ForgeEntityTypeRepository: + return ForgeEntity{ ID: p.RepoID, - EntityType: GithubEntityTypeRepository, + EntityType: ForgeEntityTypeRepository, }, nil - case GithubEntityTypeOrganization: - return GithubEntity{ + case ForgeEntityTypeOrganization: + return ForgeEntity{ ID: p.OrgID, - EntityType: GithubEntityTypeOrganization, + EntityType: ForgeEntityTypeOrganization, }, nil - case GithubEntityTypeEnterprise: - return GithubEntity{ + case ForgeEntityTypeEnterprise: + return ForgeEntity{ ID: p.EnterpriseID, - EntityType: GithubEntityTypeEnterprise, + EntityType: ForgeEntityTypeEnterprise, }, nil } - return GithubEntity{}, fmt.Errorf("pool has no associated entity") + return ForgeEntity{}, fmt.Errorf("pool has no associated entity") } -func (p *ScaleSet) ScaleSetType() GithubEntityType { +func (p *ScaleSet) ScaleSetType() ForgeEntityType { switch { case p.RepoID != "": - return GithubEntityTypeRepository + return ForgeEntityTypeRepository case p.OrgID != "": - return GithubEntityTypeOrganization + return ForgeEntityTypeOrganization case p.EnterpriseID != "": - return GithubEntityTypeEnterprise + return ForgeEntityTypeEnterprise } return "" } @@ -591,7 +591,7 @@ type Repository struct { Credentials GithubCredentials `json:"credentials,omitempty"` PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancing_type,omitempty"` - Endpoint GithubEndpoint `json:"endpoint,omitempty"` + Endpoint ForgeEndpoint `json:"endpoint,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"` // Do not serialize sensitive info. @@ -602,19 +602,23 @@ func (r Repository) CreationDateGetter() time.Time { return r.CreatedAt } -func (r Repository) GetEntity() (GithubEntity, error) { +func (r Repository) GetEntity() (ForgeEntity, error) { if r.ID == "" { - return GithubEntity{}, fmt.Errorf("repository has no ID") + return ForgeEntity{}, fmt.Errorf("repository has no ID") } - return GithubEntity{ + return ForgeEntity{ ID: r.ID, - EntityType: GithubEntityTypeRepository, + EntityType: ForgeEntityTypeRepository, Owner: r.Owner, Name: r.Name, PoolBalancerType: r.PoolBalancerType, - Credentials: r.Credentials, - WebhookSecret: r.WebhookSecret, - CreatedAt: r.CreatedAt, + Credentials: ForgeCredentials{ + ForgeType: GithubEndpointType, + GithubCredentials: r.Credentials, + }, + WebhookSecret: r.WebhookSecret, + CreatedAt: r.CreatedAt, + UpdatedAt: r.UpdatedAt, }, nil } @@ -652,7 +656,7 @@ type Organization struct { CredentialsID uint `json:"credentials_id,omitempty"` PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancing_type,omitempty"` - Endpoint GithubEndpoint `json:"endpoint,omitempty"` + Endpoint ForgeEndpoint `json:"endpoint,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"` // Do not serialize sensitive info. @@ -663,18 +667,22 @@ func (o Organization) GetCreatedAt() time.Time { return o.CreatedAt } -func (o Organization) GetEntity() (GithubEntity, error) { +func (o Organization) GetEntity() (ForgeEntity, error) { if o.ID == "" { - return GithubEntity{}, fmt.Errorf("organization has no ID") + return ForgeEntity{}, fmt.Errorf("organization has no ID") } - return GithubEntity{ + return ForgeEntity{ ID: o.ID, - EntityType: GithubEntityTypeOrganization, + EntityType: ForgeEntityTypeOrganization, Owner: o.Name, WebhookSecret: o.WebhookSecret, PoolBalancerType: o.PoolBalancerType, - Credentials: o.Credentials, - CreatedAt: o.CreatedAt, + Credentials: ForgeCredentials{ + ForgeType: GithubEndpointType, + GithubCredentials: o.Credentials, + }, + CreatedAt: o.CreatedAt, + UpdatedAt: o.UpdatedAt, }, nil } @@ -708,7 +716,7 @@ type Enterprise struct { CredentialsID uint `json:"credentials_id,omitempty"` PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancing_type,omitempty"` - Endpoint GithubEndpoint `json:"endpoint,omitempty"` + Endpoint ForgeEndpoint `json:"endpoint,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"` // Do not serialize sensitive info. @@ -719,18 +727,22 @@ func (e Enterprise) GetCreatedAt() time.Time { return e.CreatedAt } -func (e Enterprise) GetEntity() (GithubEntity, error) { +func (e Enterprise) GetEntity() (ForgeEntity, error) { if e.ID == "" { - return GithubEntity{}, fmt.Errorf("enterprise has no ID") + return ForgeEntity{}, fmt.Errorf("enterprise has no ID") } - return GithubEntity{ + return ForgeEntity{ ID: e.ID, - EntityType: GithubEntityTypeEnterprise, + EntityType: ForgeEntityTypeEnterprise, Owner: e.Name, WebhookSecret: e.WebhookSecret, PoolBalancerType: e.PoolBalancerType, - Credentials: e.Credentials, - CreatedAt: e.CreatedAt, + Credentials: ForgeCredentials{ + ForgeType: GithubEndpointType, + GithubCredentials: e.Credentials, + }, + CreatedAt: e.CreatedAt, + UpdatedAt: e.UpdatedAt, }, nil } @@ -843,20 +855,113 @@ func (g GithubRateLimit) ResetAt() time.Time { return time.Unix(g.Reset, 0) } +type ForgeCredentials struct { + ForgeType EndpointType `json:"type,omitempty"` + GithubCredentials GithubCredentials `json:"github,omitempty"` +} + +func (f ForgeCredentials) CABundle() []byte { + switch f.ForgeType { + case GithubEndpointType: + return f.GithubCredentials.CABundle + case GiteaEndpointType: + return nil + default: + return nil + } +} + +func (f ForgeCredentials) Endpoint() ForgeEndpoint { + switch f.ForgeType { + case GithubEndpointType: + return f.GithubCredentials.Endpoint + case GiteaEndpointType: + return ForgeEndpoint{} + default: + return ForgeEndpoint{} + } +} + +func (f ForgeCredentials) APIBaseURL() string { + switch f.ForgeType { + case GithubEndpointType: + return f.GithubCredentials.APIBaseURL + case GiteaEndpointType: + return "" + default: + return "" + } +} + +func (f ForgeCredentials) UploadBaseURL() string { + switch f.ForgeType { + case GithubEndpointType: + return f.GithubCredentials.UploadBaseURL + case GiteaEndpointType: + return "" + default: + return "" + } +} + +func (f ForgeCredentials) BaseURL() string { + switch f.ForgeType { + case GithubEndpointType: + return f.GithubCredentials.BaseURL + case GiteaEndpointType: + return "" + default: + return "" + } +} + +func (f ForgeCredentials) GetHTTPClient(ctx context.Context) (*http.Client, error) { + switch f.ForgeType { + case GithubEndpointType: + return f.GithubCredentials.GetHTTPClient(ctx) + case GiteaEndpointType: + return nil, fmt.Errorf("gitea credentials not supported") + default: + return nil, fmt.Errorf("unknown credentials type") + } +} + +func (f ForgeCredentials) GetID() uint { + switch f.ForgeType { + case GithubEndpointType: + return f.GithubCredentials.ID + case GiteaEndpointType: + return 0 + default: + return 0 + } +} + +func (f ForgeCredentials) RootCertificateBundle() (CertificateBundle, error) { + switch f.ForgeType { + case GithubEndpointType: + return f.GithubCredentials.RootCertificateBundle() + case GiteaEndpointType: + return CertificateBundle{}, fmt.Errorf("gitea credentials not supported") + default: + return CertificateBundle{}, fmt.Errorf("unknown credentials type") + } +} + type GithubCredentials struct { - ID uint `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - APIBaseURL string `json:"api_base_url,omitempty"` - UploadBaseURL string `json:"upload_base_url,omitempty"` - BaseURL string `json:"base_url,omitempty"` - CABundle []byte `json:"ca_bundle,omitempty"` - AuthType GithubAuthType `json:"auth-type,omitempty"` + ID uint `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + APIBaseURL string `json:"api_base_url,omitempty"` + UploadBaseURL string `json:"upload_base_url,omitempty"` + BaseURL string `json:"base_url,omitempty"` + CABundle []byte `json:"ca_bundle,omitempty"` + AuthType ForgeAuthType `json:"auth-type,omitempty"` Repositories []Repository `json:"repositories,omitempty"` Organizations []Organization `json:"organizations,omitempty"` Enterprises []Enterprise `json:"enterprises,omitempty"` - Endpoint GithubEndpoint `json:"endpoint,omitempty"` + Endpoint ForgeEndpoint `json:"endpoint,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"` RateLimit GithubRateLimit `json:"rate_limit,omitempty"` @@ -869,6 +974,13 @@ func (g GithubCredentials) GetID() uint { return g.ID } +func (g GithubCredentials) GetForgeCredentials() ForgeCredentials { + return ForgeCredentials{ + ForgeType: GithubEndpointType, + GithubCredentials: g, + } +} + func (g GithubCredentials) GetHTTPClient(ctx context.Context) (*http.Client, error) { var roots *x509.CertPool if g.CABundle != nil { @@ -888,7 +1000,7 @@ func (g GithubCredentials) GetHTTPClient(ctx context.Context) (*http.Client, err var tc *http.Client switch g.AuthType { - case GithubAuthTypeApp: + case ForgeAuthTypeApp: var app GithubApp if err := json.Unmarshal(g.CredentialsPayload, &app); err != nil { return nil, fmt.Errorf("failed to unmarshal github app credentials: %w", err) @@ -1063,64 +1175,65 @@ type UpdateSystemInfoParams struct { AgentID *int64 `json:"agent_id,omitempty"` } -type GithubEntity struct { - Owner string `json:"owner,omitempty"` - Name string `json:"name,omitempty"` - ID string `json:"id,omitempty"` - EntityType GithubEntityType `json:"entity_type,omitempty"` - Credentials GithubCredentials `json:"credentials,omitempty"` - PoolBalancerType PoolBalancerType `json:"pool_balancing_type,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty"` +type ForgeEntity struct { + Owner string `json:"owner,omitempty"` + Name string `json:"name,omitempty"` + ID string `json:"id,omitempty"` + EntityType ForgeEntityType `json:"entity_type,omitempty"` + Credentials ForgeCredentials `json:"credentials,omitempty"` + PoolBalancerType PoolBalancerType `json:"pool_balancing_type,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` WebhookSecret string `json:"-"` } -func (g GithubEntity) GetCreatedAt() time.Time { +func (g ForgeEntity) GetCreatedAt() time.Time { return g.CreatedAt } -func (g GithubEntity) GithubURL() string { +func (g ForgeEntity) ForgeURL() string { switch g.EntityType { - case GithubEntityTypeRepository: - return fmt.Sprintf("%s/%s/%s", g.Credentials.BaseURL, g.Owner, g.Name) - case GithubEntityTypeOrganization: - return fmt.Sprintf("%s/%s", g.Credentials.BaseURL, g.Owner) - case GithubEntityTypeEnterprise: - return fmt.Sprintf("%s/enterprises/%s", g.Credentials.BaseURL, g.Owner) + case ForgeEntityTypeRepository: + return fmt.Sprintf("%s/%s/%s", g.Credentials.BaseURL(), g.Owner, g.Name) + case ForgeEntityTypeOrganization: + return fmt.Sprintf("%s/%s", g.Credentials.BaseURL(), g.Owner) + case ForgeEntityTypeEnterprise: + return fmt.Sprintf("%s/enterprises/%s", g.Credentials.BaseURL(), g.Owner) } return "" } -func (g GithubEntity) GetPoolBalancerType() PoolBalancerType { +func (g ForgeEntity) GetPoolBalancerType() PoolBalancerType { if g.PoolBalancerType == "" { return PoolBalancerTypeRoundRobin } return g.PoolBalancerType } -func (g GithubEntity) LabelScope() string { +func (g ForgeEntity) LabelScope() string { switch g.EntityType { - case GithubEntityTypeRepository: + case ForgeEntityTypeRepository: return MetricsLabelRepositoryScope - case GithubEntityTypeOrganization: + case ForgeEntityTypeOrganization: return MetricsLabelOrganizationScope - case GithubEntityTypeEnterprise: + case ForgeEntityTypeEnterprise: return MetricsLabelEnterpriseScope } return "" } -func (g GithubEntity) String() string { +func (g ForgeEntity) String() string { switch g.EntityType { - case GithubEntityTypeRepository: + case ForgeEntityTypeRepository: return fmt.Sprintf("%s/%s", g.Owner, g.Name) - case GithubEntityTypeOrganization, GithubEntityTypeEnterprise: + case ForgeEntityTypeOrganization, ForgeEntityTypeEnterprise: return g.Owner } return "" } -func (g GithubEntity) GetIDAsUUID() (uuid.UUID, error) { +func (g ForgeEntity) GetIDAsUUID() (uuid.UUID, error) { if g.ID == "" { return uuid.Nil, nil } @@ -1132,9 +1245,9 @@ func (g GithubEntity) GetIDAsUUID() (uuid.UUID, error) { } // used by swagger client generated code -type GithubEndpoints []GithubEndpoint +type ForgeEndpoints []ForgeEndpoint -type GithubEndpoint struct { +type ForgeEndpoint struct { Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` APIBaseURL string `json:"api_base_url,omitempty"` @@ -1145,6 +1258,4 @@ type GithubEndpoint struct { UpdatedAt time.Time `json:"updated_at,omitempty"` EndpointType EndpointType `json:"endpoint_type,omitempty"` - - Credentials []GithubCredentials `json:"credentials,omitempty"` } diff --git a/params/requests.go b/params/requests.go index 12d9b838..7ab1fa91 100644 --- a/params/requests.go +++ b/params/requests.go @@ -448,12 +448,12 @@ func (g GithubApp) Validate() error { } type CreateGithubCredentialsParams struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Endpoint string `json:"endpoint,omitempty"` - AuthType GithubAuthType `json:"auth_type,omitempty"` - PAT GithubPAT `json:"pat,omitempty"` - App GithubApp `json:"app,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Endpoint string `json:"endpoint,omitempty"` + AuthType ForgeAuthType `json:"auth_type,omitempty"` + PAT GithubPAT `json:"pat,omitempty"` + App GithubApp `json:"app,omitempty"` } func (c CreateGithubCredentialsParams) Validate() error { @@ -466,18 +466,18 @@ func (c CreateGithubCredentialsParams) Validate() error { } switch c.AuthType { - case GithubAuthTypePAT, GithubAuthTypeApp: + case ForgeAuthTypePAT, ForgeAuthTypeApp: default: return runnerErrors.NewBadRequestError("invalid auth_type") } - if c.AuthType == GithubAuthTypePAT { + if c.AuthType == ForgeAuthTypePAT { if c.PAT.OAuth2Token == "" { return runnerErrors.NewBadRequestError("missing oauth2_token") } } - if c.AuthType == GithubAuthTypeApp { + if c.AuthType == ForgeAuthTypeApp { if err := c.App.Validate(); err != nil { return errors.Wrap(err, "invalid app") } diff --git a/runner/common/mocks/GithubClient.go b/runner/common/mocks/GithubClient.go index f1009d5a..6ba39d48 100644 --- a/runner/common/mocks/GithubClient.go +++ b/runner/common/mocks/GithubClient.go @@ -118,18 +118,18 @@ func (_m *GithubClient) DeleteEntityHook(ctx context.Context, id int64) (*github } // GetEntity provides a mock function with no fields -func (_m *GithubClient) GetEntity() params.GithubEntity { +func (_m *GithubClient) GetEntity() params.ForgeEntity { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for GetEntity") } - var r0 params.GithubEntity - if rf, ok := ret.Get(0).(func() params.GithubEntity); ok { + var r0 params.ForgeEntity + if rf, ok := ret.Get(0).(func() params.ForgeEntity); ok { r0 = rf() } else { - r0 = ret.Get(0).(params.GithubEntity) + r0 = ret.Get(0).(params.ForgeEntity) } return r0 diff --git a/runner/common/mocks/GithubEntityOperations.go b/runner/common/mocks/GithubEntityOperations.go index a482a985..567d4ebc 100644 --- a/runner/common/mocks/GithubEntityOperations.go +++ b/runner/common/mocks/GithubEntityOperations.go @@ -118,18 +118,18 @@ func (_m *GithubEntityOperations) DeleteEntityHook(ctx context.Context, id int64 } // GetEntity provides a mock function with no fields -func (_m *GithubEntityOperations) GetEntity() params.GithubEntity { +func (_m *GithubEntityOperations) GetEntity() params.ForgeEntity { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for GetEntity") } - var r0 params.GithubEntity - if rf, ok := ret.Get(0).(func() params.GithubEntity); ok { + var r0 params.ForgeEntity + if rf, ok := ret.Get(0).(func() params.ForgeEntity); ok { r0 = rf() } else { - r0 = ret.Get(0).(params.GithubEntity) + r0 = ret.Get(0).(params.ForgeEntity) } return r0 diff --git a/runner/common/util.go b/runner/common/util.go index 55e8fb00..39c03651 100644 --- a/runner/common/util.go +++ b/runner/common/util.go @@ -23,7 +23,7 @@ type GithubEntityOperations interface { GetEntityJITConfig(ctx context.Context, instance string, pool params.Pool, labels []string) (jitConfigMap map[string]string, runner *github.Runner, err error) // GetEntity returns the GitHub entity for which the github client was instanciated. - GetEntity() params.GithubEntity + GetEntity() params.ForgeEntity // GithubBaseURL returns the base URL for the github or GHES API. GithubBaseURL() *url.URL } diff --git a/runner/enterprises.go b/runner/enterprises.go index fb3f528b..6cbe54d0 100644 --- a/runner/enterprises.go +++ b/runner/enterprises.go @@ -206,9 +206,9 @@ func (r *Runner) CreateEnterprisePool(ctx context.Context, enterpriseID string, param.RunnerBootstrapTimeout = appdefaults.DefaultRunnerBootstrapTimeout } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: enterpriseID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pool, err := r.store.CreateEntityPool(ctx, entity, createPoolParams) @@ -223,9 +223,9 @@ func (r *Runner) GetEnterprisePoolByID(ctx context.Context, enterpriseID, poolID if !auth.IsAdmin(ctx) { return params.Pool{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: enterpriseID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pool, err := r.store.GetEntityPool(ctx, entity, poolID) if err != nil { @@ -239,9 +239,9 @@ func (r *Runner) DeleteEnterprisePool(ctx context.Context, enterpriseID, poolID return runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: enterpriseID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pool, err := r.store.GetEntityPool(ctx, entity, poolID) @@ -270,9 +270,9 @@ func (r *Runner) ListEnterprisePools(ctx context.Context, enterpriseID string) ( return []params.Pool{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: enterpriseID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pools, err := r.store.ListEntityPools(ctx, entity) if err != nil { @@ -286,9 +286,9 @@ func (r *Runner) UpdateEnterprisePool(ctx context.Context, enterpriseID, poolID return params.Pool{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: enterpriseID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pool, err := r.store.GetEntityPool(ctx, entity, poolID) if err != nil { @@ -320,9 +320,9 @@ func (r *Runner) ListEnterpriseInstances(ctx context.Context, enterpriseID strin if !auth.IsAdmin(ctx) { return nil, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: enterpriseID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } instances, err := r.store.ListEntityInstances(ctx, entity) if err != nil { diff --git a/runner/enterprises_test.go b/runner/enterprises_test.go index 94bc4807..7e4545d4 100644 --- a/runner/enterprises_test.go +++ b/runner/enterprises_test.go @@ -58,7 +58,7 @@ type EnterpriseTestSuite struct { testCreds params.GithubCredentials secondaryTestCreds params.GithubCredentials - githubEndpoint params.GithubEndpoint + forgeEndpoint params.ForgeEndpoint } func (s *EnterpriseTestSuite) SetupTest() { @@ -70,9 +70,9 @@ func (s *EnterpriseTestSuite) SetupTest() { } adminCtx := garmTesting.ImpersonateAdminContext(context.Background(), db, s.T()) - s.githubEndpoint = garmTesting.CreateDefaultGithubEndpoint(adminCtx, db, s.T()) - s.testCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), s.githubEndpoint) - s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "secondary-creds", db, s.T(), s.githubEndpoint) + s.forgeEndpoint = garmTesting.CreateDefaultGithubEndpoint(adminCtx, db, s.T()) + s.testCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), s.forgeEndpoint) + s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "secondary-creds", db, s.T(), s.forgeEndpoint) // create some organization objects in the database, for testing purposes enterprises := map[string]params.Enterprise{} @@ -270,9 +270,9 @@ func (s *EnterpriseTestSuite) TestDeleteEnterpriseErrUnauthorized() { } func (s *EnterpriseTestSuite) TestDeleteEnterprisePoolDefinedFailed() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreEnterprises["test-enterprise-1"].ID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -377,9 +377,9 @@ func (s *EnterpriseTestSuite) TestCreateEnterprisePoolFetchPoolParamsFailed() { } func (s *EnterpriseTestSuite) TestGetEnterprisePoolByID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreEnterprises["test-enterprise-1"].ID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } enterprisePool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -399,9 +399,9 @@ func (s *EnterpriseTestSuite) TestGetEnterprisePoolByIDErrUnauthorized() { } func (s *EnterpriseTestSuite) TestDeleteEnterprisePool() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreEnterprises["test-enterprise-1"].ID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -423,9 +423,9 @@ func (s *EnterpriseTestSuite) TestDeleteEnterprisePoolErrUnauthorized() { } func (s *EnterpriseTestSuite) TestDeleteEnterprisePoolRunnersFailed() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreEnterprises["test-enterprise-1"].ID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -442,9 +442,9 @@ func (s *EnterpriseTestSuite) TestDeleteEnterprisePoolRunnersFailed() { } func (s *EnterpriseTestSuite) TestListEnterprisePools() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreEnterprises["test-enterprise-1"].ID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } enterprisePools := []params.Pool{} for i := 1; i <= 2; i++ { @@ -469,9 +469,9 @@ func (s *EnterpriseTestSuite) TestListOrgPoolsErrUnauthorized() { } func (s *EnterpriseTestSuite) TestUpdateEnterprisePool() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreEnterprises["test-enterprise-1"].ID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } enterprisePool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -492,9 +492,9 @@ func (s *EnterpriseTestSuite) TestUpdateEnterprisePoolErrUnauthorized() { } func (s *EnterpriseTestSuite) TestUpdateEnterprisePoolMinIdleGreaterThanMax() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreEnterprises["test-enterprise-1"].ID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -511,9 +511,9 @@ func (s *EnterpriseTestSuite) TestUpdateEnterprisePoolMinIdleGreaterThanMax() { } func (s *EnterpriseTestSuite) TestListEnterpriseInstances() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreEnterprises["test-enterprise-1"].ID, - EntityType: params.GithubEntityTypeEnterprise, + EntityType: params.ForgeEntityTypeEnterprise, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { diff --git a/runner/github_endpoints.go b/runner/github_endpoints.go index 1f6431ea..3f4fb308 100644 --- a/runner/github_endpoints.go +++ b/runner/github_endpoints.go @@ -10,30 +10,30 @@ import ( "github.com/cloudbase/garm/params" ) -func (r *Runner) CreateGithubEndpoint(ctx context.Context, param params.CreateGithubEndpointParams) (params.GithubEndpoint, error) { +func (r *Runner) CreateGithubEndpoint(ctx context.Context, param params.CreateGithubEndpointParams) (params.ForgeEndpoint, error) { if !auth.IsAdmin(ctx) { - return params.GithubEndpoint{}, runnerErrors.ErrUnauthorized + return params.ForgeEndpoint{}, runnerErrors.ErrUnauthorized } if err := param.Validate(); err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "failed to validate github endpoint params") + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to validate github endpoint params") } ep, err := r.store.CreateGithubEndpoint(ctx, param) if err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "failed to create github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to create github endpoint") } return ep, nil } -func (r *Runner) GetGithubEndpoint(ctx context.Context, name string) (params.GithubEndpoint, error) { +func (r *Runner) GetGithubEndpoint(ctx context.Context, name string) (params.ForgeEndpoint, error) { if !auth.IsAdmin(ctx) { - return params.GithubEndpoint{}, runnerErrors.ErrUnauthorized + return params.ForgeEndpoint{}, runnerErrors.ErrUnauthorized } endpoint, err := r.store.GetGithubEndpoint(ctx, name) if err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "failed to get github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to get github endpoint") } return endpoint, nil @@ -52,23 +52,23 @@ func (r *Runner) DeleteGithubEndpoint(ctx context.Context, name string) error { return nil } -func (r *Runner) UpdateGithubEndpoint(ctx context.Context, name string, param params.UpdateGithubEndpointParams) (params.GithubEndpoint, error) { +func (r *Runner) UpdateGithubEndpoint(ctx context.Context, name string, param params.UpdateGithubEndpointParams) (params.ForgeEndpoint, error) { if !auth.IsAdmin(ctx) { - return params.GithubEndpoint{}, runnerErrors.ErrUnauthorized + return params.ForgeEndpoint{}, runnerErrors.ErrUnauthorized } if err := param.Validate(); err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "failed to validate github endpoint params") + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to validate github endpoint params") } newEp, err := r.store.UpdateGithubEndpoint(ctx, name, param) if err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "failed to update github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to update github endpoint") } return newEp, nil } -func (r *Runner) ListGithubEndpoints(ctx context.Context) ([]params.GithubEndpoint, error) { +func (r *Runner) ListGithubEndpoints(ctx context.Context) ([]params.ForgeEndpoint, error) { if !auth.IsAdmin(ctx) { return nil, runnerErrors.ErrUnauthorized } diff --git a/runner/metadata.go b/runner/metadata.go index 3892d350..8a9c8469 100644 --- a/runner/metadata.go +++ b/runner/metadata.go @@ -56,7 +56,7 @@ func (r *Runner) GetRunnerServiceName(ctx context.Context) (string, error) { ctx, "failed to get instance params") return "", runnerErrors.ErrUnauthorized } - var entity params.GithubEntity + var entity params.ForgeEntity switch { case instance.PoolID != "": @@ -96,11 +96,11 @@ func (r *Runner) GetRunnerServiceName(ctx context.Context) (string, error) { tpl := "actions.runner.%s.%s" var serviceName string switch entity.EntityType { - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: serviceName = fmt.Sprintf(tpl, entity.Owner, instance.Name) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: serviceName = fmt.Sprintf(tpl, entity.Owner, instance.Name) - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: serviceName = fmt.Sprintf(tpl, fmt.Sprintf("%s-%s", entity.Owner, entity.Name), instance.Name) } return serviceName, nil diff --git a/runner/organizations.go b/runner/organizations.go index 4b5e3fd7..49f143ea 100644 --- a/runner/organizations.go +++ b/runner/organizations.go @@ -235,9 +235,9 @@ func (r *Runner) CreateOrgPool(ctx context.Context, orgID string, param params.C param.RunnerBootstrapTimeout = appdefaults.DefaultRunnerBootstrapTimeout } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: orgID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pool, err := r.store.CreateEntityPool(ctx, entity, createPoolParams) @@ -253,9 +253,9 @@ func (r *Runner) GetOrgPoolByID(ctx context.Context, orgID, poolID string) (para return params.Pool{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: orgID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pool, err := r.store.GetEntityPool(ctx, entity, poolID) @@ -271,9 +271,9 @@ func (r *Runner) DeleteOrgPool(ctx context.Context, orgID, poolID string) error return runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: orgID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pool, err := r.store.GetEntityPool(ctx, entity, poolID) @@ -304,9 +304,9 @@ func (r *Runner) ListOrgPools(ctx context.Context, orgID string) ([]params.Pool, if !auth.IsAdmin(ctx) { return []params.Pool{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: orgID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pools, err := r.store.ListEntityPools(ctx, entity) if err != nil { @@ -320,9 +320,9 @@ func (r *Runner) UpdateOrgPool(ctx context.Context, orgID, poolID string, param return params.Pool{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: orgID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pool, err := r.store.GetEntityPool(ctx, entity, poolID) @@ -356,9 +356,9 @@ func (r *Runner) ListOrgInstances(ctx context.Context, orgID string) ([]params.I return nil, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: orgID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } instances, err := r.store.ListEntityInstances(ctx, entity) diff --git a/runner/organizations_test.go b/runner/organizations_test.go index ae0af3cf..3609b941 100644 --- a/runner/organizations_test.go +++ b/runner/organizations_test.go @@ -58,7 +58,7 @@ type OrgTestSuite struct { testCreds params.GithubCredentials secondaryTestCreds params.GithubCredentials - githubEndpoint params.GithubEndpoint + githubEndpoint params.ForgeEndpoint } func (s *OrgTestSuite) SetupTest() { @@ -284,9 +284,9 @@ func (s *OrgTestSuite) TestDeleteOrganizationErrUnauthorized() { } func (s *OrgTestSuite) TestDeleteOrganizationPoolDefinedFailed() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreOrgs["test-org-1"].ID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -402,9 +402,9 @@ func (s *OrgTestSuite) TestCreateOrgPoolFetchPoolParamsFailed() { } func (s *OrgTestSuite) TestGetOrgPoolByID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreOrgs["test-org-1"].ID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } orgPool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -424,9 +424,9 @@ func (s *OrgTestSuite) TestGetOrgPoolByIDErrUnauthorized() { } func (s *OrgTestSuite) TestDeleteOrgPool() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreOrgs["test-org-1"].ID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -448,9 +448,9 @@ func (s *OrgTestSuite) TestDeleteOrgPoolErrUnauthorized() { } func (s *OrgTestSuite) TestDeleteOrgPoolRunnersFailed() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreOrgs["test-org-1"].ID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -467,9 +467,9 @@ func (s *OrgTestSuite) TestDeleteOrgPoolRunnersFailed() { } func (s *OrgTestSuite) TestListOrgPools() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreOrgs["test-org-1"].ID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } orgPools := []params.Pool{} for i := 1; i <= 2; i++ { @@ -494,9 +494,9 @@ func (s *OrgTestSuite) TestListOrgPoolsErrUnauthorized() { } func (s *OrgTestSuite) TestUpdateOrgPool() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreOrgs["test-org-1"].ID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } orgPool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -517,9 +517,9 @@ func (s *OrgTestSuite) TestUpdateOrgPoolErrUnauthorized() { } func (s *OrgTestSuite) TestUpdateOrgPoolMinIdleGreaterThanMax() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreOrgs["test-org-1"].ID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -536,9 +536,9 @@ func (s *OrgTestSuite) TestUpdateOrgPoolMinIdleGreaterThanMax() { } func (s *OrgTestSuite) TestListOrgInstances() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreOrgs["test-org-1"].ID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { diff --git a/runner/pool/pool.go b/runner/pool/pool.go index 3cb8bff3..e24aa69b 100644 --- a/runner/pool/pool.go +++ b/runner/pool/pool.go @@ -67,7 +67,7 @@ const ( maxCreateAttempts = 5 ) -func NewEntityPoolManager(ctx context.Context, entity params.GithubEntity, instanceTokenGetter auth.InstanceTokenGetter, providers map[string]common.Provider, store dbCommon.Store) (common.PoolManager, error) { +func NewEntityPoolManager(ctx context.Context, entity params.ForgeEntity, instanceTokenGetter auth.InstanceTokenGetter, providers map[string]common.Provider, store dbCommon.Store) (common.PoolManager, error) { ctx = garmUtil.WithSlogContext(ctx, slog.Any("pool_mgr", entity.String()), slog.Any("pool_type", entity.EntityType)) ghc, err := ghClient.Client(ctx, entity) if err != nil { @@ -83,7 +83,7 @@ func NewEntityPoolManager(ctx context.Context, entity params.GithubEntity, insta return nil, errors.Wrap(err, "getting controller info") } - consumerID := fmt.Sprintf("pool-manager-%s-%s", entity.String(), entity.Credentials.Endpoint.Name) + consumerID := fmt.Sprintf("pool-manager-%s-%s", entity.String(), entity.Credentials.Endpoint().Name) slog.InfoContext(ctx, "registering consumer", "consumer_id", consumerID) consumer, err := watcher.RegisterConsumer( ctx, consumerID, @@ -120,7 +120,7 @@ func NewEntityPoolManager(ctx context.Context, entity params.GithubEntity, insta type basePoolManager struct { ctx context.Context consumerID string - entity params.GithubEntity + entity params.ForgeEntity ghcli common.GithubClient controllerInfo params.ControllerInfo instanceTokenGetter auth.InstanceTokenGetter @@ -877,7 +877,7 @@ func (r *basePoolManager) addInstanceToProvider(instance params.Instance) error bootstrapArgs := commonParams.BootstrapInstance{ Name: instance.Name, Tools: r.tools, - RepoURL: r.entity.GithubURL(), + RepoURL: r.entity.ForgeURL(), MetadataURL: instance.MetadataURL, CallbackURL: instance.CallbackURL, InstanceToken: jwtToken, @@ -887,7 +887,7 @@ func (r *basePoolManager) addInstanceToProvider(instance params.Instance) error Image: pool.Image, ExtraSpecs: pool.ExtraSpecs, PoolID: instance.PoolID, - CACertBundle: r.entity.Credentials.CABundle, + CACertBundle: r.entity.Credentials.CABundle(), GitHubRunnerGroup: instance.GitHubRunnerGroup, JitConfigEnabled: hasJITConfig, } @@ -981,11 +981,11 @@ func (r *basePoolManager) paramsWorkflowJobToParamsJob(job params.WorkflowJob) ( } switch r.entity.EntityType { - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: jobParams.EnterpriseID = &asUUID - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: jobParams.RepoID = &asUUID - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: jobParams.OrgID = &asUUID default: return jobParams, errors.Errorf("unknown pool type: %s", r.entity.EntityType) @@ -1931,15 +1931,15 @@ func (r *basePoolManager) InstallWebhook(ctx context.Context, param params.Insta func (r *basePoolManager) ValidateOwner(job params.WorkflowJob) error { switch r.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: 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: + case params.ForgeEntityTypeOrganization: if !strings.EqualFold(job.Organization.Login, r.entity.Owner) { return runnerErrors.NewBadRequestError("job not meant for this pool manager") } - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: if !strings.EqualFold(job.Enterprise.Slug, r.entity.Owner) { return runnerErrors.NewBadRequestError("job not meant for this pool manager") } diff --git a/runner/pool/stub_client.go b/runner/pool/stub_client.go index 2518ce9c..e8f1e7c6 100644 --- a/runner/pool/stub_client.go +++ b/runner/pool/stub_client.go @@ -57,8 +57,8 @@ func (s *stubGithubClient) GetWorkflowJobByID(_ context.Context, _, _ string, _ return nil, nil, s.err } -func (s *stubGithubClient) GetEntity() params.GithubEntity { - return params.GithubEntity{} +func (s *stubGithubClient) GetEntity() params.ForgeEntity { + return params.ForgeEntity{} } func (s *stubGithubClient) GithubBaseURL() *url.URL { diff --git a/runner/pool/util.go b/runner/pool/util.go index 9b7b7f14..25fdc73f 100644 --- a/runner/pool/util.go +++ b/runner/pool/util.go @@ -119,7 +119,7 @@ func isManagedRunner(labels []string, controllerID string) bool { return runnerControllerID == controllerID } -func composeWatcherFilters(entity params.GithubEntity) dbCommon.PayloadFilterFunc { +func composeWatcherFilters(entity params.ForgeEntity) dbCommon.PayloadFilterFunc { // We want to watch for changes in either the controller or the // entity itself. return watcher.WithAny( @@ -131,6 +131,6 @@ func composeWatcherFilters(entity params.GithubEntity) dbCommon.PayloadFilterFun // Any operation on the entity we're managing the pool for. watcher.WithEntityFilter(entity), // Watch for changes to the github credentials - watcher.WithGithubCredentialsFilter(entity.Credentials), + watcher.WithForgeCredentialsFilter(entity.Credentials), ) } diff --git a/runner/pool/watcher.go b/runner/pool/watcher.go index 7f05d93b..56427e89 100644 --- a/runner/pool/watcher.go +++ b/runner/pool/watcher.go @@ -14,7 +14,7 @@ import ( // entityGetter is implemented by all github entities (repositories, organizations and enterprises) type entityGetter interface { - GetEntity() (params.GithubEntity, error) + GetEntity() (params.ForgeEntity, error) } func (r *basePoolManager) handleControllerUpdateEvent(controllerInfo params.ControllerInfo) { @@ -38,7 +38,7 @@ func (r *basePoolManager) getClientOrStub() runnerCommon.GithubClient { return ghc } -func (r *basePoolManager) handleEntityUpdate(entity params.GithubEntity, operation common.OperationType) { +func (r *basePoolManager) handleEntityUpdate(entity params.ForgeEntity, operation common.OperationType) { slog.DebugContext(r.ctx, "received entity operation", "entity", entity.ID, "operation", operation) if r.entity.ID != entity.ID { slog.WarnContext(r.ctx, "entity ID mismatch; stale event? refusing to update", "entity", entity.ID) @@ -56,7 +56,7 @@ func (r *basePoolManager) handleEntityUpdate(entity params.GithubEntity, operati return } - credentialsUpdate := r.entity.Credentials.ID != entity.Credentials.ID + credentialsUpdate := r.entity.Credentials.GetID() != entity.Credentials.GetID() defer func() { slog.DebugContext(r.ctx, "deferred tools update", "credentials_update", credentialsUpdate) if !credentialsUpdate { @@ -85,7 +85,7 @@ func (r *basePoolManager) handleEntityUpdate(entity params.GithubEntity, operati slog.DebugContext(r.ctx, "lock released", "entity", entity.ID) } -func (r *basePoolManager) handleCredentialsUpdate(credentials params.GithubCredentials) { +func (r *basePoolManager) handleCredentialsUpdate(credentials params.ForgeCredentials) { // when we switch credentials on an entity (like from one app to another or from an app // to a PAT), we may still get events for the previous credentials as the channel is buffered. // The watcher will watch for changes to the entity itself, which includes events that @@ -97,12 +97,12 @@ func (r *basePoolManager) handleCredentialsUpdate(credentials params.GithubCrede // test-repo. This function would handle situations where "org_pat" is updated. // If "test-repo" is updated with new credentials, that event is handled above in // handleEntityUpdate. - shouldUpdateTools := r.entity.Credentials.ID == credentials.ID + shouldUpdateTools := r.entity.Credentials.GetID() == credentials.GetID() defer func() { if !shouldUpdateTools { return } - slog.DebugContext(r.ctx, "deferred tools update", "credentials_id", credentials.ID) + slog.DebugContext(r.ctx, "deferred tools update", "credentials_id", credentials.GetID()) if err := r.updateTools(); err != nil { slog.ErrorContext(r.ctx, "failed to update tools", "error", err) } @@ -110,12 +110,12 @@ func (r *basePoolManager) handleCredentialsUpdate(credentials params.GithubCrede r.mux.Lock() if !shouldUpdateTools { - slog.InfoContext(r.ctx, "credential ID mismatch; stale event?", "credentials_id", credentials.ID) + slog.InfoContext(r.ctx, "credential ID mismatch; stale event?", "credentials_id", credentials.GetID()) r.mux.Unlock() return } - slog.DebugContext(r.ctx, "updating credentials", "credentials_id", credentials.ID) + slog.DebugContext(r.ctx, "updating credentials", "credentials_id", credentials.GetID()) r.entity.Credentials = credentials r.ghcli = r.getClientOrStub() r.mux.Unlock() @@ -130,7 +130,7 @@ func (r *basePoolManager) handleWatcherEvent(event common.ChangePayload) { slog.ErrorContext(r.ctx, "failed to cast payload to github credentials") return } - r.handleCredentialsUpdate(credentials) + r.handleCredentialsUpdate(credentials.GetForgeCredentials()) case common.ControllerEntityType: controllerInfo, ok := event.Payload.(params.ControllerInfo) if !ok { diff --git a/runner/pools_test.go b/runner/pools_test.go index 918598d1..95c6b6bd 100644 --- a/runner/pools_test.go +++ b/runner/pools_test.go @@ -49,7 +49,7 @@ type PoolTestSuite struct { adminCtx context.Context testCreds params.GithubCredentials secondaryTestCreds params.GithubCredentials - githubEndpoint params.GithubEndpoint + githubEndpoint params.ForgeEndpoint } func (s *PoolTestSuite) SetupTest() { @@ -75,9 +75,9 @@ func (s *PoolTestSuite) SetupTest() { } // create some pool objects in the database, for testing purposes - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: org.ID, - EntityType: params.GithubEntityTypeOrganization, + EntityType: params.ForgeEntityTypeOrganization, } orgPools := []params.Pool{} for i := 1; i <= 3; i++ { diff --git a/runner/repositories.go b/runner/repositories.go index ab4f8e90..83876c5d 100644 --- a/runner/repositories.go +++ b/runner/repositories.go @@ -235,9 +235,9 @@ func (r *Runner) CreateRepoPool(ctx context.Context, repoID string, param params createPoolParams.RunnerBootstrapTimeout = appdefaults.DefaultRunnerBootstrapTimeout } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: repoID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := r.store.CreateEntityPool(ctx, entity, createPoolParams) @@ -253,9 +253,9 @@ func (r *Runner) GetRepoPoolByID(ctx context.Context, repoID, poolID string) (pa return params.Pool{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: repoID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := r.store.GetEntityPool(ctx, entity, poolID) @@ -271,9 +271,9 @@ func (r *Runner) DeleteRepoPool(ctx context.Context, repoID, poolID string) erro return runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: repoID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := r.store.GetEntityPool(ctx, entity, poolID) if err != nil { @@ -300,9 +300,9 @@ func (r *Runner) ListRepoPools(ctx context.Context, repoID string) ([]params.Poo if !auth.IsAdmin(ctx) { return []params.Pool{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: repoID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pools, err := r.store.ListEntityPools(ctx, entity) if err != nil { @@ -328,9 +328,9 @@ func (r *Runner) UpdateRepoPool(ctx context.Context, repoID, poolID string, para return params.Pool{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: repoID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := r.store.GetEntityPool(ctx, entity, poolID) if err != nil { @@ -362,9 +362,9 @@ func (r *Runner) ListRepoInstances(ctx context.Context, repoID string) ([]params if !auth.IsAdmin(ctx) { return nil, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: repoID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } instances, err := r.store.ListEntityInstances(ctx, entity) if err != nil { diff --git a/runner/repositories_test.go b/runner/repositories_test.go index c1aa04b4..47bfb003 100644 --- a/runner/repositories_test.go +++ b/runner/repositories_test.go @@ -62,7 +62,7 @@ type RepoTestSuite struct { testCreds params.GithubCredentials secondaryTestCreds params.GithubCredentials - githubEndpoint params.GithubEndpoint + githubEndpoint params.ForgeEndpoint } func (s *RepoTestSuite) SetupTest() { @@ -299,9 +299,9 @@ func (s *RepoTestSuite) TestDeleteRepositoryErrUnauthorized() { } func (s *RepoTestSuite) TestDeleteRepositoryPoolDefinedFailed() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreRepos["test-repo-1"].ID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -419,9 +419,9 @@ func (s *RepoTestSuite) TestCreateRepoPoolFetchPoolParamsFailed() { } func (s *RepoTestSuite) TestGetRepoPoolByID() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreRepos["test-repo-1"].ID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } repoPool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -441,9 +441,9 @@ func (s *RepoTestSuite) TestGetRepoPoolByIDErrUnauthorized() { } func (s *RepoTestSuite) TestDeleteRepoPool() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreRepos["test-repo-1"].ID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -465,9 +465,9 @@ func (s *RepoTestSuite) TestDeleteRepoPoolErrUnauthorized() { } func (s *RepoTestSuite) TestDeleteRepoPoolRunnersFailed() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreRepos["test-repo-1"].ID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -484,9 +484,9 @@ func (s *RepoTestSuite) TestDeleteRepoPoolRunnersFailed() { } func (s *RepoTestSuite) TestListRepoPools() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreRepos["test-repo-1"].ID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } repoPools := []params.Pool{} for i := 1; i <= 2; i++ { @@ -511,9 +511,9 @@ func (s *RepoTestSuite) TestListRepoPoolsErrUnauthorized() { } func (s *RepoTestSuite) TestListPoolInstances() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreRepos["test-repo-1"].ID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -542,9 +542,9 @@ func (s *RepoTestSuite) TestListPoolInstancesErrUnauthorized() { } func (s *RepoTestSuite) TestUpdateRepoPool() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreRepos["test-repo-1"].ID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } repoPool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -565,9 +565,9 @@ func (s *RepoTestSuite) TestUpdateRepoPoolErrUnauthorized() { } func (s *RepoTestSuite) TestUpdateRepoPoolMinIdleGreaterThanMax() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreRepos["test-repo-1"].ID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { @@ -584,9 +584,9 @@ func (s *RepoTestSuite) TestUpdateRepoPoolMinIdleGreaterThanMax() { } func (s *RepoTestSuite) TestListRepoInstances() { - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: s.Fixtures.StoreRepos["test-repo-1"].ID, - EntityType: params.GithubEntityTypeRepository, + EntityType: params.ForgeEntityTypeRepository, } pool, err := s.Fixtures.Store.CreateEntityPool(s.Fixtures.AdminContext, entity, s.Fixtures.CreatePoolParams) if err != nil { diff --git a/runner/runner.go b/runner/runner.go index 42a955fc..6d5bc5eb 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -602,10 +602,10 @@ func (r *Runner) validateHookBody(signature, secret string, body []byte) error { return nil } -func (r *Runner) findEndpointForJob(job params.WorkflowJob) (params.GithubEndpoint, error) { +func (r *Runner) findEndpointForJob(job params.WorkflowJob) (params.ForgeEndpoint, error) { uri, err := url.ParseRequestURI(job.WorkflowJob.HTMLURL) if err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "parsing job URL") + return params.ForgeEndpoint{}, errors.Wrap(err, "parsing job URL") } baseURI := fmt.Sprintf("%s://%s", uri.Scheme, uri.Host) @@ -616,7 +616,7 @@ func (r *Runner) findEndpointForJob(job params.WorkflowJob) (params.GithubEndpoi // that much about the performance of this function. endpoints, err := r.store.ListGithubEndpoints(r.ctx) if err != nil { - return params.GithubEndpoint{}, errors.Wrap(err, "fetching github endpoints") + return params.ForgeEndpoint{}, errors.Wrap(err, "fetching github endpoints") } for _, ep := range endpoints { if ep.BaseURL == baseURI { @@ -624,7 +624,7 @@ func (r *Runner) findEndpointForJob(job params.WorkflowJob) (params.GithubEndpoi } } - return params.GithubEndpoint{}, runnerErrors.NewNotFoundError("no endpoint found for job") + return params.ForgeEndpoint{}, runnerErrors.NewNotFoundError("no endpoint found for job") } func (r *Runner) DispatchWorkflowJob(hookTargetType, signature string, jobData []byte) error { @@ -928,7 +928,7 @@ func (r *Runner) getGHCliFromInstance(ctx context.Context, instance params.Insta } // Fetching the entity from the database will populate all fields, including credentials. - entity, err = r.store.GetGithubEntity(ctx, entity.EntityType, entity.ID) + entity, err = r.store.GetForgeEntity(ctx, entity.EntityType, entity.ID) if err != nil { return nil, nil, errors.Wrap(err, "fetching entity") } diff --git a/runner/scalesets.go b/runner/scalesets.go index 83432e63..6796dc2d 100644 --- a/runner/scalesets.go +++ b/runner/scalesets.go @@ -80,7 +80,7 @@ func (r *Runner) DeleteScaleSetByID(ctx context.Context, scaleSetID uint) error return errors.Wrap(err, "getting entity") } - entity, err := r.store.GetGithubEntity(ctx, paramEntity.EntityType, paramEntity.ID) + entity, err := r.store.GetForgeEntity(ctx, paramEntity.EntityType, paramEntity.ID) if err != nil { return errors.Wrap(err, "getting entity") } @@ -143,7 +143,7 @@ func (r *Runner) UpdateScaleSetByID(ctx context.Context, scaleSetID uint, param return params.ScaleSet{}, errors.Wrap(err, "getting entity") } - entity, err := r.store.GetGithubEntity(ctx, paramEntity.EntityType, paramEntity.ID) + entity, err := r.store.GetForgeEntity(ctx, paramEntity.EntityType, paramEntity.ID) if err != nil { return params.ScaleSet{}, errors.Wrap(err, "getting entity") } @@ -198,7 +198,7 @@ func (r *Runner) UpdateScaleSetByID(ctx context.Context, scaleSetID uint, param return newScaleSet, nil } -func (r *Runner) CreateEntityScaleSet(ctx context.Context, entityType params.GithubEntityType, entityID string, param params.CreateScaleSetParams) (scaleSetRet params.ScaleSet, err error) { +func (r *Runner) CreateEntityScaleSet(ctx context.Context, entityType params.ForgeEntityType, entityID string, param params.CreateScaleSetParams) (scaleSetRet params.ScaleSet, err error) { if !auth.IsAdmin(ctx) { return params.ScaleSet{}, runnerErrors.ErrUnauthorized } @@ -211,7 +211,7 @@ func (r *Runner) CreateEntityScaleSet(ctx context.Context, entityType params.Git param.GitHubRunnerGroup = "Default" } - entity, err := r.store.GetGithubEntity(ctx, entityType, entityID) + entity, err := r.store.GetForgeEntity(ctx, entityType, entityID) if err != nil { return params.ScaleSet{}, errors.Wrap(err, "getting entity") } @@ -287,11 +287,11 @@ func (r *Runner) ListScaleSetInstances(ctx context.Context, scalesetID uint) ([] return instances, nil } -func (r *Runner) ListEntityScaleSets(ctx context.Context, entityType params.GithubEntityType, entityID string) ([]params.ScaleSet, error) { +func (r *Runner) ListEntityScaleSets(ctx context.Context, entityType params.ForgeEntityType, entityID string) ([]params.ScaleSet, error) { if !auth.IsAdmin(ctx) { return []params.ScaleSet{}, runnerErrors.ErrUnauthorized } - entity := params.GithubEntity{ + entity := params.ForgeEntity{ ID: entityID, EntityType: entityType, } diff --git a/test/integration/client_utils.go b/test/integration/client_utils.go index a0f17893..977cc11c 100644 --- a/test/integration/client_utils.go +++ b/test/integration/client_utils.go @@ -77,7 +77,7 @@ func updateGithubCredentials(apiCli *client.GarmAPI, apiAuthToken runtime.Client return &updateCredentialsResponse.Payload, nil } -func createGithubEndpoint(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, endpointParams params.CreateGithubEndpointParams) (*params.GithubEndpoint, error) { +func createGithubEndpoint(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, endpointParams params.CreateGithubEndpointParams) (*params.ForgeEndpoint, error) { createEndpointResponse, err := apiCli.Endpoints.CreateGithubEndpoint( clientEndpoints.NewCreateGithubEndpointParams().WithBody(endpointParams), apiAuthToken) @@ -87,7 +87,7 @@ func createGithubEndpoint(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAut return &createEndpointResponse.Payload, nil } -func listGithubEndpoints(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter) (params.GithubEndpoints, error) { +func listGithubEndpoints(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter) (params.ForgeEndpoints, error) { listEndpointsResponse, err := apiCli.Endpoints.ListGithubEndpoints( clientEndpoints.NewListGithubEndpointsParams(), apiAuthToken) @@ -97,7 +97,7 @@ func listGithubEndpoints(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuth return listEndpointsResponse.Payload, nil } -func getGithubEndpoint(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, endpointName string) (*params.GithubEndpoint, error) { +func getGithubEndpoint(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, endpointName string) (*params.ForgeEndpoint, error) { getEndpointResponse, err := apiCli.Endpoints.GetGithubEndpoint( clientEndpoints.NewGetGithubEndpointParams().WithName(endpointName), apiAuthToken) @@ -113,7 +113,7 @@ func deleteGithubEndpoint(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAut apiAuthToken) } -func updateGithubEndpoint(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, endpointName string, endpointParams params.UpdateGithubEndpointParams) (*params.GithubEndpoint, error) { +func updateGithubEndpoint(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, endpointName string, endpointParams params.UpdateGithubEndpointParams) (*params.ForgeEndpoint, error) { updateEndpointResponse, err := apiCli.Endpoints.UpdateGithubEndpoint( clientEndpoints.NewUpdateGithubEndpointParams().WithName(endpointName).WithBody(endpointParams), apiAuthToken) diff --git a/test/integration/credentials_test.go b/test/integration/credentials_test.go index 8d92bf22..f7c9c691 100644 --- a/test/integration/credentials_test.go +++ b/test/integration/credentials_test.go @@ -25,7 +25,7 @@ func (suite *GarmSuite) TestGithubCredentialsErrorOnDuplicateCredentialsName() { Name: dummyCredentialsName, Endpoint: defaultEndpointName, Description: "GARM test credentials", - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "dummy", }, @@ -68,7 +68,7 @@ func (suite *GarmSuite) TestGithubCredentialsFailsOnInvalidAuthType() { Name: dummyCredentialsName, Endpoint: defaultEndpointName, Description: "GARM test credentials", - AuthType: params.GithubAuthType("invalid"), + AuthType: params.ForgeAuthType("invalid"), PAT: params.GithubPAT{ OAuth2Token: "dummy", }, @@ -87,7 +87,7 @@ func (suite *GarmSuite) TestGithubCredentialsFailsWhenAuthTypeParamsAreIncorrect Name: dummyCredentialsName, Endpoint: defaultEndpointName, Description: "GARM test credentials", - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, App: params.GithubApp{ AppID: 123, InstallationID: 456, @@ -107,7 +107,7 @@ func (suite *GarmSuite) TestGithubCredentialsFailsWhenAuthTypeParamsAreMissing() Name: dummyCredentialsName, Endpoint: defaultEndpointName, Description: "GARM test credentials", - AuthType: params.GithubAuthTypeApp, + AuthType: params.ForgeAuthTypeApp, } _, err := createGithubCredentials(suite.cli, suite.authToken, createCredsParams) suite.Error(err, "expected error when creating credentials with missing auth type params") @@ -147,7 +147,7 @@ func (suite *GarmSuite) TestGithubCredentialsFailWhenAppKeyIsInvalid() { Name: dummyCredentialsName, Endpoint: defaultEndpointName, Description: "GARM test credentials", - AuthType: params.GithubAuthTypeApp, + AuthType: params.ForgeAuthTypeApp, App: params.GithubApp{ AppID: 123, InstallationID: 456, @@ -166,7 +166,7 @@ func (suite *GarmSuite) TestGithubCredentialsFailWhenEndpointDoesntExist() { Name: dummyCredentialsName, Endpoint: "iDontExist.example.com", Description: "GARM test credentials", - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "dummy", }, @@ -189,7 +189,7 @@ func (suite *GarmSuite) TestGithubCredentialsFailsOnDuplicateName() { Name: dummyCredentialsName, Endpoint: defaultEndpointName, Description: "GARM test credentials", - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "dummy", }, @@ -204,7 +204,7 @@ func (suite *GarmSuite) createDummyCredentials(name, endpointName string) (*para Name: name, Endpoint: endpointName, Description: "GARM test credentials", - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: "dummy", }, diff --git a/test/integration/endpoints.go b/test/integration/endpoints.go index 9e47d854..9f1320ca 100644 --- a/test/integration/endpoints.go +++ b/test/integration/endpoints.go @@ -8,7 +8,7 @@ import ( "github.com/cloudbase/garm/params" ) -func checkEndpointParamsAreEqual(a, b params.GithubEndpoint) error { +func checkEndpointParamsAreEqual(a, b params.ForgeEndpoint) error { if a.Name != b.Name { return fmt.Errorf("endpoint name mismatch") } diff --git a/test/integration/endpoints_test.go b/test/integration/endpoints_test.go index e09916bc..a958259f 100644 --- a/test/integration/endpoints_test.go +++ b/test/integration/endpoints_test.go @@ -163,7 +163,7 @@ func (suite *GarmSuite) MustDefaultGithubEndpoint() { suite.Equal(ep.Name, "github.com", "default GitHub endpoint name mismatch") } -func (suite *GarmSuite) GetGithubEndpoint(name string) *params.GithubEndpoint { +func (suite *GarmSuite) GetGithubEndpoint(name string) *params.ForgeEndpoint { t := suite.T() t.Log("Get GitHub endpoint") endpoint, err := getGithubEndpoint(suite.cli, suite.authToken, name) @@ -172,7 +172,7 @@ func (suite *GarmSuite) GetGithubEndpoint(name string) *params.GithubEndpoint { return endpoint } -func (suite *GarmSuite) CreateGithubEndpoint(params params.CreateGithubEndpointParams) (*params.GithubEndpoint, error) { +func (suite *GarmSuite) CreateGithubEndpoint(params params.CreateGithubEndpointParams) (*params.ForgeEndpoint, error) { t := suite.T() t.Log("Create GitHub endpoint") endpoint, err := createGithubEndpoint(suite.cli, suite.authToken, params) @@ -190,7 +190,7 @@ func (suite *GarmSuite) DeleteGithubEndpoint(name string) error { return nil } -func (suite *GarmSuite) ListGithubEndpoints() params.GithubEndpoints { +func (suite *GarmSuite) ListGithubEndpoints() params.ForgeEndpoints { t := suite.T() t.Log("List GitHub endpoints") endpoints, err := listGithubEndpoints(suite.cli, suite.authToken) @@ -199,7 +199,7 @@ func (suite *GarmSuite) ListGithubEndpoints() params.GithubEndpoints { return endpoints } -func (suite *GarmSuite) createDummyEndpoint(name string) (*params.GithubEndpoint, error) { +func (suite *GarmSuite) createDummyEndpoint(name string) (*params.ForgeEndpoint, error) { endpointParams := params.CreateGithubEndpointParams{ Name: name, Description: "Dummy endpoint", diff --git a/test/integration/repositories_test.go b/test/integration/repositories_test.go index 7b396ffc..1f111fcc 100644 --- a/test/integration/repositories_test.go +++ b/test/integration/repositories_test.go @@ -22,7 +22,7 @@ func (suite *GarmSuite) EnsureTestCredentials(name string, oauthToken string, en Name: name, Endpoint: endpointName, Description: "GARM test credentials", - AuthType: params.GithubAuthTypePAT, + AuthType: params.ForgeAuthTypePAT, PAT: params.GithubPAT{ OAuth2Token: oauthToken, }, diff --git a/util/github/client.go b/util/github/client.go index 77803f4f..1480561a 100644 --- a/util/github/client.go +++ b/util/github/client.go @@ -22,6 +22,7 @@ import ( "log/slog" "net/http" "net/url" + "strings" "github.com/google/go-github/v71/github" "github.com/pkg/errors" @@ -39,7 +40,7 @@ type githubClient struct { enterprise *github.EnterpriseService rateLimit *github.RateLimitService - entity params.GithubEntity + entity params.ForgeEntity cli *github.Client } @@ -57,9 +58,9 @@ func (g *githubClient) ListEntityHooks(ctx context.Context, opts *github.ListOpt } }() switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ret, response, err = g.repo.ListHooks(ctx, g.entity.Owner, g.entity.Name, opts) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ret, response, err = g.org.ListHooks(ctx, g.entity.Owner, opts) default: return nil, nil, fmt.Errorf("invalid entity type: %s", g.entity.EntityType) @@ -81,9 +82,9 @@ func (g *githubClient) GetEntityHook(ctx context.Context, id int64) (ret *github } }() switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ret, _, err = g.repo.GetHook(ctx, g.entity.Owner, g.entity.Name, id) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ret, _, err = g.org.GetHook(ctx, g.entity.Owner, id) default: return nil, errors.New("invalid entity type") @@ -105,9 +106,9 @@ func (g *githubClient) CreateEntityHook(ctx context.Context, hook *github.Hook) } }() switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ret, _, err = g.repo.CreateHook(ctx, g.entity.Owner, g.entity.Name, hook) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ret, _, err = g.org.CreateHook(ctx, g.entity.Owner, hook) default: return nil, errors.New("invalid entity type") @@ -129,9 +130,9 @@ func (g *githubClient) DeleteEntityHook(ctx context.Context, id int64) (ret *git } }() switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ret, err = g.repo.DeleteHook(ctx, g.entity.Owner, g.entity.Name, id) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ret, err = g.org.DeleteHook(ctx, g.entity.Owner, id) default: return nil, errors.New("invalid entity type") @@ -153,9 +154,9 @@ func (g *githubClient) PingEntityHook(ctx context.Context, id int64) (ret *githu } }() switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ret, err = g.repo.PingHook(ctx, g.entity.Owner, g.entity.Name, id) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ret, err = g.org.PingHook(ctx, g.entity.Owner, id) default: return nil, errors.New("invalid entity type") @@ -182,11 +183,11 @@ func (g *githubClient) ListEntityRunners(ctx context.Context, opts *github.ListR }() switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ret, response, err = g.ListRunners(ctx, g.entity.Owner, g.entity.Name, opts) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ret, response, err = g.ListOrganizationRunners(ctx, g.entity.Owner, opts) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: ret, response, err = g.enterprise.ListRunners(ctx, g.entity.Owner, opts) default: return nil, nil, errors.New("invalid entity type") @@ -214,11 +215,11 @@ func (g *githubClient) ListEntityRunnerApplicationDownloads(ctx context.Context) }() switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ret, response, err = g.ListRunnerApplicationDownloads(ctx, g.entity.Owner, g.entity.Name) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ret, response, err = g.ListOrganizationRunnerApplicationDownloads(ctx, g.entity.Owner) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: ret, response, err = g.enterprise.ListRunnerApplicationDownloads(ctx, g.entity.Owner) default: return nil, nil, errors.New("invalid entity type") @@ -277,11 +278,11 @@ func (g *githubClient) RemoveEntityRunner(ctx context.Context, runnerID int64) e }() switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: response, err = g.RemoveRunner(ctx, g.entity.Owner, g.entity.Name, runnerID) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: response, err = g.RemoveOrganizationRunner(ctx, g.entity.Owner, runnerID) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: response, err = g.enterprise.RemoveRunner(ctx, g.entity.Owner, runnerID) default: return errors.New("invalid entity type") @@ -313,11 +314,11 @@ func (g *githubClient) CreateEntityRegistrationToken(ctx context.Context) (*gith }() switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ret, response, err = g.CreateRegistrationToken(ctx, g.entity.Owner, g.entity.Name) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ret, response, err = g.CreateOrganizationRegistrationToken(ctx, g.entity.Owner) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: ret, response, err = g.enterprise.CreateRegistrationToken(ctx, g.entity.Owner) default: return nil, nil, errors.New("invalid entity type") @@ -326,7 +327,7 @@ func (g *githubClient) CreateEntityRegistrationToken(ctx context.Context) (*gith return ret, response, err } -func (g *githubClient) getOrganizationRunnerGroupIDByName(ctx context.Context, entity params.GithubEntity, rgName string) (int64, error) { +func (g *githubClient) getOrganizationRunnerGroupIDByName(ctx context.Context, entity params.ForgeEntity, rgName string) (int64, error) { opts := github.ListOrgRunnerGroupOptions{ ListOptions: github.ListOptions{ PerPage: 100, @@ -362,7 +363,7 @@ func (g *githubClient) getOrganizationRunnerGroupIDByName(ctx context.Context, e return 0, runnerErrors.NewNotFoundError("runner group %s not found", rgName) } -func (g *githubClient) getEnterpriseRunnerGroupIDByName(ctx context.Context, entity params.GithubEntity, rgName string) (int64, error) { +func (g *githubClient) getEnterpriseRunnerGroupIDByName(ctx context.Context, entity params.ForgeEntity, rgName string) (int64, error) { opts := github.ListEnterpriseRunnerGroupOptions{ ListOptions: github.ListOptions{ PerPage: 100, @@ -405,9 +406,9 @@ func (g *githubClient) GetEntityJITConfig(ctx context.Context, instance string, if pool.GitHubRunnerGroup != "" { switch g.entity.EntityType { - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: rgID, err = g.getOrganizationRunnerGroupIDByName(ctx, g.entity, pool.GitHubRunnerGroup) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: rgID, err = g.getEnterpriseRunnerGroupIDByName(ctx, g.entity, pool.GitHubRunnerGroup) } @@ -434,11 +435,11 @@ func (g *githubClient) GetEntityJITConfig(ctx context.Context, instance string, var response *github.Response switch g.entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: ret, response, err = g.GenerateRepoJITConfig(ctx, g.entity.Owner, g.entity.Name, &req) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: ret, response, err = g.GenerateOrgJITConfig(ctx, g.entity.Owner, &req) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: ret, response, err = g.enterprise.GenerateEnterpriseJITConfig(ctx, g.entity.Owner, &req) } if err != nil { @@ -482,7 +483,7 @@ func (g *githubClient) RateLimit(ctx context.Context) (*github.RateLimits, error return limits, nil } -func (g *githubClient) GetEntity() params.GithubEntity { +func (g *githubClient) GetEntity() params.ForgeEntity { return g.entity } @@ -514,8 +515,49 @@ func NewRateLimitClient(ctx context.Context, credentials params.GithubCredential return cli, nil } -func Client(ctx context.Context, entity params.GithubEntity) (common.GithubClient, error) { - // func GithubClient(ctx context.Context, entity params.GithubEntity) (common.GithubClient, error) { +func withGiteaURLs(client *github.Client, apiBaseURL, uploadBaseURL string) (*github.Client, error) { + if client == nil { + return nil, errors.New("client is nil") + } + + if apiBaseURL == "" || uploadBaseURL == "" { + return nil, errors.New("invalid gitea URLs") + } + + parsedBaseURL, err := url.ParseRequestURI(apiBaseURL) + if err != nil { + return nil, errors.Wrap(err, "parsing gitea base URL") + } + + if !strings.HasSuffix(parsedBaseURL.Path, "/") { + parsedBaseURL.Path += "/" + } + + if !strings.HasSuffix(parsedBaseURL.Path, "/api/v1/") { + parsedBaseURL.Path += "api/v1/" + } + + parsedUploadURL, err := url.ParseRequestURI(uploadBaseURL) + if err != nil { + return nil, errors.Wrap(err, "parsing gitea upload URL") + } + + if !strings.HasSuffix(parsedUploadURL.Path, "/") { + parsedUploadURL.Path += "/" + } + + if !strings.HasSuffix(parsedUploadURL.Path, "/api/v1/") { + parsedUploadURL.Path += "api/v1/" + } + + client.BaseURL = parsedBaseURL + client.UploadURL = parsedUploadURL + + return client, nil +} + +func Client(ctx context.Context, entity params.ForgeEntity) (common.GithubClient, error) { + // func GithubClient(ctx context.Context, entity params.ForgeEntity) (common.GithubClient, error) { httpClient, err := entity.Credentials.GetHTTPClient(ctx) if err != nil { return nil, errors.Wrap(err, "fetching http client") @@ -523,11 +565,17 @@ func Client(ctx context.Context, entity params.GithubEntity) (common.GithubClien slog.DebugContext( ctx, "creating client for entity", - "entity", entity.String(), "base_url", entity.Credentials.APIBaseURL, - "upload_url", entity.Credentials.UploadBaseURL) + "entity", entity.String(), "base_url", entity.Credentials.APIBaseURL(), + "upload_url", entity.Credentials.UploadBaseURL()) + + ghClient := github.NewClient(httpClient) + switch entity.Credentials.ForgeType { + case params.GithubEndpointType: + ghClient, err = ghClient.WithEnterpriseURLs(entity.Credentials.APIBaseURL(), entity.Credentials.UploadBaseURL()) + case params.GiteaEndpointType: + ghClient, err = withGiteaURLs(ghClient, entity.Credentials.APIBaseURL(), entity.Credentials.UploadBaseURL()) + } - ghClient, err := github.NewClient(httpClient).WithEnterpriseURLs( - entity.Credentials.APIBaseURL, entity.Credentials.UploadBaseURL) if err != nil { return nil, errors.Wrap(err, "fetching github client") } diff --git a/util/github/scalesets/token.go b/util/github/scalesets/token.go index 47aa764f..1491b748 100644 --- a/util/github/scalesets/token.go +++ b/util/github/scalesets/token.go @@ -36,7 +36,7 @@ func (s *ScaleSetClient) getActionServiceInfo(ctx context.Context) (params.Actio entity := s.ghCli.GetEntity() body := params.ActionsServiceAdminInfoRequest{ - URL: entity.GithubURL(), + URL: entity.ForgeURL(), RunnerEvent: "register", } diff --git a/workers/cache/cache.go b/workers/cache/cache.go index 13400a3a..ce23d269 100644 --- a/workers/cache/cache.go +++ b/workers/cache/cache.go @@ -262,7 +262,7 @@ func (w *Worker) handleEntityEvent(entityGetter params.EntityGetter, op common.O w.toolsWorkes[entity.ID] = worker } else if hasOld { // probably an update operation - if old.Credentials.ID != entity.Credentials.ID { + if old.Credentials.GetID() != entity.Credentials.GetID() { worker.Reset() } } diff --git a/workers/cache/tool_cache.go b/workers/cache/tool_cache.go index 6133580d..7d8d5737 100644 --- a/workers/cache/tool_cache.go +++ b/workers/cache/tool_cache.go @@ -16,7 +16,7 @@ import ( "github.com/cloudbase/garm/util/github" ) -func newToolsUpdater(ctx context.Context, entity params.GithubEntity) *toolsUpdater { +func newToolsUpdater(ctx context.Context, entity params.ForgeEntity) *toolsUpdater { return &toolsUpdater{ ctx: ctx, entity: entity, @@ -27,7 +27,7 @@ func newToolsUpdater(ctx context.Context, entity params.GithubEntity) *toolsUpda type toolsUpdater struct { ctx context.Context - entity params.GithubEntity + entity params.ForgeEntity tools []commonParams.RunnerApplicationDownload lastUpdate time.Time diff --git a/workers/entity/util.go b/workers/entity/util.go index 95c9b2cc..877758a7 100644 --- a/workers/entity/util.go +++ b/workers/entity/util.go @@ -31,7 +31,7 @@ func composeControllerWatcherFilters() dbCommon.PayloadFilterFunc { ) } -func composeWorkerWatcherFilters(entity params.GithubEntity) dbCommon.PayloadFilterFunc { +func composeWorkerWatcherFilters(entity params.ForgeEntity) dbCommon.PayloadFilterFunc { return watcher.WithAny( watcher.WithAll( watcher.WithEntityFilter(entity), @@ -39,7 +39,7 @@ func composeWorkerWatcherFilters(entity params.GithubEntity) dbCommon.PayloadFil ), // Watch for credentials updates. watcher.WithAll( - watcher.WithGithubCredentialsFilter(entity.Credentials), + watcher.WithForgeCredentialsFilter(entity.Credentials), watcher.WithOperationTypeFilter(dbCommon.UpdateOperation), ), ) diff --git a/workers/entity/worker.go b/workers/entity/worker.go index fcfd2a60..7f0f79e6 100644 --- a/workers/entity/worker.go +++ b/workers/entity/worker.go @@ -7,6 +7,8 @@ import ( "sync" "time" + "golang.org/x/sync/errgroup" + "github.com/cloudbase/garm/cache" dbCommon "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/database/watcher" @@ -16,10 +18,9 @@ import ( "github.com/cloudbase/garm/util/github" "github.com/cloudbase/garm/util/github/scalesets" "github.com/cloudbase/garm/workers/scaleset" - "golang.org/x/sync/errgroup" ) -func NewWorker(ctx context.Context, store dbCommon.Store, entity params.GithubEntity, providers map[string]common.Provider) (*Worker, error) { +func NewWorker(ctx context.Context, store dbCommon.Store, entity params.ForgeEntity, providers map[string]common.Provider) (*Worker, error) { consumerID := fmt.Sprintf("entity-worker-%s", entity.String()) ctx = garmUtil.WithSlogContext( @@ -43,7 +44,7 @@ type Worker struct { store dbCommon.Store ghCli common.GithubClient - Entity params.GithubEntity + Entity params.ForgeEntity providers map[string]common.Provider scaleSetController *scaleset.Controller diff --git a/workers/entity/worker_watcher.go b/workers/entity/worker_watcher.go index be0b6b3c..04e20a65 100644 --- a/workers/entity/worker_watcher.go +++ b/workers/entity/worker_watcher.go @@ -44,7 +44,7 @@ func (w *Worker) handleEntityEventPayload(event dbCommon.ChangePayload) { defer w.mux.Unlock() credentials := entity.Credentials - if w.Entity.Credentials.ID != credentials.ID { + if w.Entity.Credentials.GetID() != credentials.GetID() { // credentials were swapped on the entity. We need to recompose the watcher // filters. w.consumer.SetFilters(composeWorkerWatcherFilters(entity)) @@ -63,18 +63,29 @@ func (w *Worker) handleEntityEventPayload(event dbCommon.ChangePayload) { } func (w *Worker) handleEntityCredentialsEventPayload(event dbCommon.ChangePayload) { - credentials, ok := event.Payload.(params.GithubCredentials) + var credsGetter params.ForgeCredentialsGetter + var ok bool + + switch event.EntityType { + case dbCommon.GithubCredentialsEntityType: + credsGetter, ok = event.Payload.(params.GithubCredentials) + default: + slog.ErrorContext(w.ctx, "invalid entity type", "entity_type", event.EntityType) + return + } if !ok { slog.ErrorContext(w.ctx, "invalid payload for entity type", "entity_type", event.EntityType, "payload", event.Payload) return } + credentials := credsGetter.GetForgeCredentials() + switch event.Operation { case dbCommon.UpdateOperation: slog.DebugContext(w.ctx, "got delete operation") w.mux.Lock() defer w.mux.Unlock() - if w.Entity.Credentials.ID != credentials.ID { + if w.Entity.Credentials.GetID() != credentials.GetID() { // The channel is buffered. We may get an old update. If credentials get updated // immediately after they are swapped on the entity, we may still get an update // pushed to the channel before the filters are swapped. We can ignore the update. diff --git a/workers/pools/controller.go b/workers/pools/controller.go index 458766a9..058ebec6 100644 --- a/workers/pools/controller.go +++ b/workers/pools/controller.go @@ -1,3 +1 @@ package pools - - diff --git a/workers/provider/instance_manager.go b/workers/provider/instance_manager.go index 37680cd0..9ba94553 100644 --- a/workers/provider/instance_manager.go +++ b/workers/provider/instance_manager.go @@ -52,7 +52,7 @@ type instanceManager struct { helper providerHelper scaleSet params.ScaleSet - scaleSetEntity params.GithubEntity + scaleSetEntity params.ForgeEntity deleteBackoff time.Duration @@ -120,14 +120,14 @@ func (i *instanceManager) incrementBackOff() { } } -func (i *instanceManager) getEntity() (params.GithubEntity, error) { +func (i *instanceManager) getEntity() (params.ForgeEntity, error) { entity, err := i.scaleSet.GetEntity() if err != nil { - return params.GithubEntity{}, fmt.Errorf("getting entity: %w", err) + return params.ForgeEntity{}, fmt.Errorf("getting entity: %w", err) } ghEntity, err := i.helper.GetGithubEntity(entity) if err != nil { - return params.GithubEntity{}, fmt.Errorf("getting entity: %w", err) + return params.ForgeEntity{}, fmt.Errorf("getting entity: %w", err) } return ghEntity, nil } @@ -156,7 +156,7 @@ func (i *instanceManager) handleCreateInstanceInProvider(instance params.Instanc bootstrapArgs := commonParams.BootstrapInstance{ Name: instance.Name, Tools: tools, - RepoURL: entity.GithubURL(), + RepoURL: entity.ForgeURL(), MetadataURL: instance.MetadataURL, CallbackURL: instance.CallbackURL, InstanceToken: token, @@ -167,7 +167,7 @@ func (i *instanceManager) handleCreateInstanceInProvider(instance params.Instanc ExtraSpecs: i.scaleSet.ExtraSpecs, // This is temporary. We need to extend providers to know about scale sets. PoolID: i.pseudoPoolID(), - CACertBundle: entity.Credentials.CABundle, + CACertBundle: entity.Credentials.CABundle(), GitHubRunnerGroup: i.scaleSet.GitHubRunnerGroup, JitConfigEnabled: true, } diff --git a/workers/provider/provider_helper.go b/workers/provider/provider_helper.go index 6a53bab3..96762135 100644 --- a/workers/provider/provider_helper.go +++ b/workers/provider/provider_helper.go @@ -14,7 +14,7 @@ type providerHelper interface { InstanceTokenGetter() auth.InstanceTokenGetter updateArgsFromProviderInstance(instanceName string, providerInstance commonParams.ProviderInstance) (params.Instance, error) GetControllerInfo() (params.ControllerInfo, error) - GetGithubEntity(entity params.GithubEntity) (params.GithubEntity, error) + GetGithubEntity(entity params.ForgeEntity) (params.ForgeEntity, error) } func (p *Provider) updateArgsFromProviderInstance(instanceName string, providerInstance commonParams.ProviderInstance) (params.Instance, error) { @@ -71,10 +71,10 @@ func (p *Provider) InstanceTokenGetter() auth.InstanceTokenGetter { return p.tokenGetter } -func (p *Provider) GetGithubEntity(entity params.GithubEntity) (params.GithubEntity, error) { - ghEntity, err := p.store.GetGithubEntity(p.ctx, entity.EntityType, entity.ID) +func (p *Provider) GetGithubEntity(entity params.ForgeEntity) (params.ForgeEntity, error) { + ghEntity, err := p.store.GetForgeEntity(p.ctx, entity.EntityType, entity.ID) if err != nil { - return params.GithubEntity{}, fmt.Errorf("getting github entity: %w", err) + return params.ForgeEntity{}, fmt.Errorf("getting github entity: %w", err) } return ghEntity, nil diff --git a/workers/scaleset/controller.go b/workers/scaleset/controller.go index 6e3170a0..45dfbfa3 100644 --- a/workers/scaleset/controller.go +++ b/workers/scaleset/controller.go @@ -15,7 +15,7 @@ import ( garmUtil "github.com/cloudbase/garm/util" ) -func NewController(ctx context.Context, store dbCommon.Store, entity params.GithubEntity, providers map[string]common.Provider) (*Controller, error) { +func NewController(ctx context.Context, store dbCommon.Store, entity params.ForgeEntity, providers map[string]common.Provider) (*Controller, error) { consumerID := fmt.Sprintf("scaleset-controller-%s", entity.String()) ctx = garmUtil.WithSlogContext( @@ -57,7 +57,7 @@ type Controller struct { ScaleSets map[uint]*scaleSet - Entity params.GithubEntity + Entity params.ForgeEntity consumer dbCommon.Consumer store dbCommon.Store diff --git a/workers/scaleset/controller_watcher.go b/workers/scaleset/controller_watcher.go index ec4771fc..551d711d 100644 --- a/workers/scaleset/controller_watcher.go +++ b/workers/scaleset/controller_watcher.go @@ -127,11 +127,11 @@ func (c *Controller) handleEntityEvent(event dbCommon.ChangePayload) { var entityGetter params.EntityGetter var ok bool switch c.Entity.EntityType { - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: entityGetter, ok = event.Payload.(params.Repository) - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: entityGetter, ok = event.Payload.(params.Organization) - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: entityGetter, ok = event.Payload.(params.Enterprise) } if !ok { diff --git a/workers/scaleset/scaleset_helper.go b/workers/scaleset/scaleset_helper.go index 26f845ff..54d9b52e 100644 --- a/workers/scaleset/scaleset_helper.go +++ b/workers/scaleset/scaleset_helper.go @@ -60,11 +60,11 @@ func (w *Worker) recordOrUpdateJob(job params.ScaleSetJobMessage) error { jobParams.RunnerGroupName = w.scaleSet.GitHubRunnerGroup switch entity.EntityType { - case params.GithubEntityTypeEnterprise: + case params.ForgeEntityTypeEnterprise: jobParams.EnterpriseID = &asUUID - case params.GithubEntityTypeRepository: + case params.ForgeEntityTypeRepository: jobParams.RepoID = &asUUID - case params.GithubEntityTypeOrganization: + case params.ForgeEntityTypeOrganization: jobParams.OrgID = &asUUID default: return fmt.Errorf("unknown entity type: %s", entity.EntityType) diff --git a/workers/scaleset/util.go b/workers/scaleset/util.go index 02d33b69..1d8d6c51 100644 --- a/workers/scaleset/util.go +++ b/workers/scaleset/util.go @@ -6,7 +6,7 @@ import ( "github.com/cloudbase/garm/params" ) -func composeControllerWatcherFilters(entity params.GithubEntity) dbCommon.PayloadFilterFunc { +func composeControllerWatcherFilters(entity params.ForgeEntity) dbCommon.PayloadFilterFunc { return watcher.WithAny( watcher.WithAll( watcher.WithEntityScaleSetFilter(entity), From 823a9e4b82189284877f93d7c04ac90c39cd24fa Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 14 May 2025 00:34:54 +0000 Subject: [PATCH 04/21] Add Gitea endpoints and credentials Signed-off-by: Gabriel Adrian Samfira --- apiserver/controllers/gitea_credentials.go | 228 +++++++++ apiserver/controllers/gitea_endpoints.go | 186 +++++++ .../{credentials.go => github_credentials.go} | 6 +- .../{endpoints.go => github_endpoints.go} | 8 +- apiserver/routers/routers.go | 38 ++ apiserver/swagger-models.yaml | 44 +- apiserver/swagger.yaml | 278 ++++++++++- cache/cache_test.go | 29 +- cache/credentials_cache.go | 66 ++- cache/entity_cache.go | 27 +- cache/tools_cache.go | 19 +- .../create_credentials_responses.go | 6 +- .../create_gitea_credentials_parameters.go | 151 ++++++ .../create_gitea_credentials_responses.go | 179 +++++++ client/credentials/credentials_client.go | 198 ++++++++ .../delete_gitea_credentials_parameters.go | 152 ++++++ .../delete_gitea_credentials_responses.go | 106 ++++ .../credentials/get_credentials_responses.go | 6 +- .../get_gitea_credentials_parameters.go | 152 ++++++ .../get_gitea_credentials_responses.go | 179 +++++++ .../list_gitea_credentials_parameters.go | 128 +++++ .../list_gitea_credentials_responses.go | 179 +++++++ .../update_credentials_responses.go | 6 +- .../update_gitea_credentials_parameters.go | 174 +++++++ .../update_gitea_credentials_responses.go | 179 +++++++ .../create_gitea_endpoint_parameters.go | 151 ++++++ .../create_gitea_endpoint_responses.go | 184 +++++++ .../create_github_endpoint_responses.go | 2 +- .../delete_gitea_endpoint_parameters.go | 151 ++++++ .../delete_gitea_endpoint_responses.go | 106 ++++ client/endpoints/endpoints_client.go | 194 ++++++++ .../get_gitea_endpoint_parameters.go | 151 ++++++ .../endpoints/get_gitea_endpoint_responses.go | 184 +++++++ .../get_github_endpoint_responses.go | 2 +- .../list_gitea_endpoints_parameters.go | 128 +++++ .../list_gitea_endpoints_responses.go | 184 +++++++ .../list_github_endpoints_responses.go | 2 +- .../update_gitea_endpoint_parameters.go | 173 +++++++ .../update_gitea_endpoint_responses.go | 184 +++++++ .../update_github_endpoint_responses.go | 2 +- cmd/garm-cli/cmd/gitea.go | 21 + cmd/garm-cli/cmd/gitea_credentials.go | 317 ++++++++++++ cmd/garm-cli/cmd/gitea_endpoints.go | 218 ++++++++ cmd/garm-cli/cmd/github_credentials.go | 4 +- cmd/garm-cli/cmd/github_endpoints.go | 10 +- cmd/garm-cli/cmd/repository.go | 7 +- cmd/garm/main.go | 1 + database/common/mocks/Store.go | 416 +++++++++++++--- database/common/store.go | 31 +- database/common/watcher.go | 1 + database/sql/enterprise.go | 2 - database/sql/enterprise_test.go | 4 +- database/sql/gitea.go | 469 ++++++++++++++++++ database/sql/github.go | 140 ++---- database/sql/github_test.go | 2 +- database/sql/models.go | 78 +-- database/sql/organizations.go | 3 - database/sql/organizations_test.go | 4 +- database/sql/pools_test.go | 4 +- database/sql/repositories.go | 41 +- database/sql/repositories_test.go | 12 +- database/sql/scalesets_test.go | 4 +- database/sql/sql.go | 1 + database/sql/util.go | 171 ++++++- database/watcher/filters.go | 2 +- database/watcher/watcher_store_test.go | 12 +- go.mod | 1 + go.sum | 2 + internal/testing/testing.go | 2 +- params/params.go | 222 +++------ params/requests.go | 204 +++++++- runner/common/mocks/GithubClient.go | 40 +- runner/common/mocks/GithubEntityOperations.go | 40 +- runner/common/mocks/RateLimitClient.go | 59 +++ runner/enterprises_test.go | 8 +- runner/gitea_credentials.go | 86 ++++ runner/gitea_endpoints.go | 82 +++ runner/github_credentials.go | 24 +- runner/organizations_test.go | 8 +- runner/pool/pool.go | 21 +- runner/pool/watcher.go | 4 +- runner/pools_test.go | 4 +- runner/repositories.go | 13 +- runner/repositories_test.go | 10 +- test/integration/client_utils.go | 4 +- test/integration/credentials_test.go | 4 +- util/github/client.go | 37 +- vendor/golang.org/x/mod/LICENSE | 27 + vendor/golang.org/x/mod/PATENTS | 22 + vendor/golang.org/x/mod/semver/semver.go | 401 +++++++++++++++ vendor/modules.txt | 3 + workers/cache/cache.go | 37 +- workers/cache/gitea_tools.go | 152 ++++++ workers/cache/tool_cache.go | 70 ++- workers/entity/controller.go | 1 + workers/entity/worker.go | 1 + workers/entity/worker_watcher.go | 7 +- workers/provider/instance_manager.go | 2 +- workers/scaleset/controller.go | 2 +- workers/scaleset/scaleset.go | 2 +- 100 files changed, 7439 insertions(+), 660 deletions(-) create mode 100644 apiserver/controllers/gitea_credentials.go create mode 100644 apiserver/controllers/gitea_endpoints.go rename apiserver/controllers/{credentials.go => github_credentials.go} (98%) rename apiserver/controllers/{endpoints.go => github_endpoints.go} (98%) create mode 100644 client/credentials/create_gitea_credentials_parameters.go create mode 100644 client/credentials/create_gitea_credentials_responses.go create mode 100644 client/credentials/delete_gitea_credentials_parameters.go create mode 100644 client/credentials/delete_gitea_credentials_responses.go create mode 100644 client/credentials/get_gitea_credentials_parameters.go create mode 100644 client/credentials/get_gitea_credentials_responses.go create mode 100644 client/credentials/list_gitea_credentials_parameters.go create mode 100644 client/credentials/list_gitea_credentials_responses.go create mode 100644 client/credentials/update_gitea_credentials_parameters.go create mode 100644 client/credentials/update_gitea_credentials_responses.go create mode 100644 client/endpoints/create_gitea_endpoint_parameters.go create mode 100644 client/endpoints/create_gitea_endpoint_responses.go create mode 100644 client/endpoints/delete_gitea_endpoint_parameters.go create mode 100644 client/endpoints/delete_gitea_endpoint_responses.go create mode 100644 client/endpoints/get_gitea_endpoint_parameters.go create mode 100644 client/endpoints/get_gitea_endpoint_responses.go create mode 100644 client/endpoints/list_gitea_endpoints_parameters.go create mode 100644 client/endpoints/list_gitea_endpoints_responses.go create mode 100644 client/endpoints/update_gitea_endpoint_parameters.go create mode 100644 client/endpoints/update_gitea_endpoint_responses.go create mode 100644 cmd/garm-cli/cmd/gitea.go create mode 100644 cmd/garm-cli/cmd/gitea_credentials.go create mode 100644 cmd/garm-cli/cmd/gitea_endpoints.go create mode 100644 database/sql/gitea.go create mode 100644 runner/common/mocks/RateLimitClient.go create mode 100644 runner/gitea_credentials.go create mode 100644 runner/gitea_endpoints.go create mode 100644 vendor/golang.org/x/mod/LICENSE create mode 100644 vendor/golang.org/x/mod/PATENTS create mode 100644 vendor/golang.org/x/mod/semver/semver.go create mode 100644 workers/cache/gitea_tools.go diff --git a/apiserver/controllers/gitea_credentials.go b/apiserver/controllers/gitea_credentials.go new file mode 100644 index 00000000..e1be0fb7 --- /dev/null +++ b/apiserver/controllers/gitea_credentials.go @@ -0,0 +1,228 @@ +package controllers + +import ( + "encoding/json" + "log/slog" + "math" + "net/http" + "strconv" + + "github.com/gorilla/mux" + + gErrors "github.com/cloudbase/garm-provider-common/errors" + "github.com/cloudbase/garm/params" +) + +// swagger:route GET /gitea/credentials credentials ListGiteaCredentials +// +// List all credentials. +// +// Responses: +// 200: Credentials +// 400: APIErrorResponse +func (a *APIController) ListGiteaCredentials(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + creds, err := a.r.ListGiteaCredentials(ctx) + if err != nil { + handleError(ctx, w, err) + return + } + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(creds); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} + +// swagger:route POST /gitea/credentials credentials CreateGiteaCredentials +// +// Create a Gitea credential. +// +// Parameters: +// + name: Body +// description: Parameters used when creating a Gitea credential. +// type: CreateGiteaCredentialsParams +// in: body +// required: true +// +// Responses: +// 200: ForgeCredentials +// 400: APIErrorResponse +func (a *APIController) CreateGiteaCredential(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var params params.CreateGiteaCredentialsParams + if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to decode request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + cred, err := a.r.CreateGiteaCredentials(ctx, params) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to create Gitea credential") + handleError(ctx, w, err) + return + } + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(cred); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} + +// swagger:route GET /gitea/credentials/{id} credentials GetGiteaCredentials +// +// Get a Gitea credential. +// +// Parameters: +// + name: id +// description: ID of the Gitea credential. +// type: integer +// in: path +// required: true +// +// Responses: +// 200: ForgeCredentials +// 400: APIErrorResponse +func (a *APIController) GetGiteaCredential(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + vars := mux.Vars(r) + idParam, ok := vars["id"] + if !ok { + slog.ErrorContext(ctx, "missing id in request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + id, err := strconv.ParseUint(idParam, 10, 64) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to parse id") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + if id > math.MaxUint { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "id is too large") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + cred, err := a.r.GetGiteaCredentials(ctx, uint(id)) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to get Gitea credential") + handleError(ctx, w, err) + return + } + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(cred); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} + +// swagger:route DELETE /gitea/credentials/{id} credentials DeleteGiteaCredentials +// +// Delete a Gitea credential. +// +// Parameters: +// + name: id +// description: ID of the Gitea credential. +// type: integer +// in: path +// required: true +// +// Responses: +// default: APIErrorResponse +func (a *APIController) DeleteGiteaCredential(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + vars := mux.Vars(r) + idParam, ok := vars["id"] + if !ok { + slog.ErrorContext(ctx, "missing id in request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + id, err := strconv.ParseUint(idParam, 10, 64) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to parse id") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + if id > math.MaxUint { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "id is too large") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + if err := a.r.DeleteGiteaCredentials(ctx, uint(id)); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to delete Gitea credential") + handleError(ctx, w, err) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +// swagger:route PUT /gitea/credentials/{id} credentials UpdateGiteaCredentials +// +// Update a Gitea credential. +// +// Parameters: +// + name: id +// description: ID of the Gitea credential. +// type: integer +// in: path +// required: true +// + name: Body +// description: Parameters used when updating a Gitea credential. +// type: UpdateGiteaCredentialsParams +// in: body +// required: true +// +// Responses: +// 200: ForgeCredentials +// 400: APIErrorResponse +func (a *APIController) UpdateGiteaCredential(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + vars := mux.Vars(r) + idParam, ok := vars["id"] + if !ok { + slog.ErrorContext(ctx, "missing id in request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + id, err := strconv.ParseUint(idParam, 10, 64) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to parse id") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + if id > math.MaxUint { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "id is too large") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + var params params.UpdateGiteaCredentialsParams + if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to decode request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + cred, err := a.r.UpdateGiteaCredentials(ctx, uint(id), params) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to update Gitea credential") + handleError(ctx, w, err) + return + } + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(cred); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} diff --git a/apiserver/controllers/gitea_endpoints.go b/apiserver/controllers/gitea_endpoints.go new file mode 100644 index 00000000..6f1525d5 --- /dev/null +++ b/apiserver/controllers/gitea_endpoints.go @@ -0,0 +1,186 @@ +package controllers + +import ( + "encoding/json" + "log/slog" + "net/http" + + "github.com/gorilla/mux" + + gErrors "github.com/cloudbase/garm-provider-common/errors" + "github.com/cloudbase/garm/params" +) + +// swagger:route POST /gitea/endpoints endpoints CreateGiteaEndpoint +// +// Create a Gitea Endpoint. +// +// Parameters: +// + name: Body +// description: Parameters used when creating a Gitea endpoint. +// type: CreateGiteaEndpointParams +// in: body +// required: true +// +// Responses: +// 200: ForgeEndpoint +// default: APIErrorResponse +func (a *APIController) CreateGiteaEndpoint(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var params params.CreateGiteaEndpointParams + if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to decode request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + endpoint, err := a.r.CreateGiteaEndpoint(ctx, params) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to create Gitea endpoint") + handleError(ctx, w, err) + return + } + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(endpoint); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} + +// swagger:route GET /gitea/endpoints endpoints ListGiteaEndpoints +// +// List all Gitea Endpoints. +// +// Responses: +// 200: ForgeEndpoints +// default: APIErrorResponse +func (a *APIController) ListGiteaEndpoints(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + endpoints, err := a.r.ListGiteaEndpoints(ctx) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to list Gitea endpoints") + handleError(ctx, w, err) + return + } + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(endpoints); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} + +// swagger:route GET /gitea/endpoints/{name} endpoints GetGiteaEndpoint +// +// Get a Gitea Endpoint. +// +// Parameters: +// + name: name +// description: The name of the Gitea endpoint. +// type: string +// in: path +// required: true +// +// Responses: +// 200: ForgeEndpoint +// default: APIErrorResponse +func (a *APIController) GetGiteaEndpoint(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + vars := mux.Vars(r) + name, ok := vars["name"] + if !ok { + slog.ErrorContext(ctx, "missing name in request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + endpoint, err := a.r.GetGiteaEndpoint(ctx, name) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to get Gitea endpoint") + handleError(ctx, w, err) + return + } + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(endpoint); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} + +// swagger:route DELETE /gitea/endpoints/{name} endpoints DeleteGiteaEndpoint +// +// Delete a Gitea Endpoint. +// +// Parameters: +// + name: name +// description: The name of the Gitea endpoint. +// type: string +// in: path +// required: true +// +// Responses: +// default: APIErrorResponse +func (a *APIController) DeleteGiteaEndpoint(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + vars := mux.Vars(r) + name, ok := vars["name"] + if !ok { + slog.ErrorContext(ctx, "missing name in request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + if err := a.r.DeleteGiteaEndpoint(ctx, name); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to delete Gitea endpoint") + handleError(ctx, w, err) + return + } + w.WriteHeader(http.StatusNoContent) +} + +// swagger:route PUT /gitea/endpoints/{name} endpoints UpdateGiteaEndpoint +// +// Update a Gitea Endpoint. +// +// Parameters: +// + name: name +// description: The name of the Gitea endpoint. +// type: string +// in: path +// required: true +// + name: Body +// description: Parameters used when updating a Gitea endpoint. +// type: UpdateGiteaEndpointParams +// in: body +// required: true +// +// Responses: +// 200: ForgeEndpoint +// default: APIErrorResponse +func (a *APIController) UpdateGiteaEndpoint(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + vars := mux.Vars(r) + name, ok := vars["name"] + if !ok { + slog.ErrorContext(ctx, "missing name in request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + var params params.UpdateGiteaEndpointParams + if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to decode request") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + endpoint, err := a.r.UpdateGiteaEndpoint(ctx, name, params) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to update GitHub endpoint") + handleError(ctx, w, err) + return + } + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(endpoint); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} diff --git a/apiserver/controllers/credentials.go b/apiserver/controllers/github_credentials.go similarity index 98% rename from apiserver/controllers/credentials.go rename to apiserver/controllers/github_credentials.go index 70869b54..c7544357 100644 --- a/apiserver/controllers/credentials.go +++ b/apiserver/controllers/github_credentials.go @@ -47,7 +47,7 @@ func (a *APIController) ListCredentials(w http.ResponseWriter, r *http.Request) // required: true // // Responses: -// 200: GithubCredentials +// 200: ForgeCredentials // 400: APIErrorResponse func (a *APIController) CreateGithubCredential(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -83,7 +83,7 @@ func (a *APIController) CreateGithubCredential(w http.ResponseWriter, r *http.Re // required: true // // Responses: -// 200: GithubCredentials +// 200: ForgeCredentials // 400: APIErrorResponse func (a *APIController) GetGithubCredential(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -183,7 +183,7 @@ func (a *APIController) DeleteGithubCredential(w http.ResponseWriter, r *http.Re // required: true // // Responses: -// 200: GithubCredentials +// 200: ForgeCredentials // 400: APIErrorResponse func (a *APIController) UpdateGithubCredential(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/apiserver/controllers/endpoints.go b/apiserver/controllers/github_endpoints.go similarity index 98% rename from apiserver/controllers/endpoints.go rename to apiserver/controllers/github_endpoints.go index 81e984d4..491c5716 100644 --- a/apiserver/controllers/endpoints.go +++ b/apiserver/controllers/github_endpoints.go @@ -23,7 +23,7 @@ import ( // required: true // // Responses: -// 200: GithubEndpoint +// 200: ForgeEndpoint // default: APIErrorResponse func (a *APIController) CreateGithubEndpoint(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -52,7 +52,7 @@ func (a *APIController) CreateGithubEndpoint(w http.ResponseWriter, r *http.Requ // List all GitHub Endpoints. // // Responses: -// 200: GithubEndpoints +// 200: ForgeEndpoints // default: APIErrorResponse func (a *APIController) ListGithubEndpoints(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -81,7 +81,7 @@ func (a *APIController) ListGithubEndpoints(w http.ResponseWriter, r *http.Reque // required: true // // Responses: -// 200: GithubEndpoint +// 200: ForgeEndpoint // default: APIErrorResponse func (a *APIController) GetGithubEndpoint(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -153,7 +153,7 @@ func (a *APIController) DeleteGithubEndpoint(w http.ResponseWriter, r *http.Requ // required: true // // Responses: -// 200: GithubEndpoint +// 200: ForgeEndpoint // default: APIErrorResponse func (a *APIController) UpdateGithubEndpoint(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/apiserver/routers/routers.go b/apiserver/routers/routers.go index ec135292..2036b5f1 100644 --- a/apiserver/routers/routers.go +++ b/apiserver/routers/routers.go @@ -454,6 +454,44 @@ func NewAPIRouter(han *controllers.APIController, authMiddleware, initMiddleware apiRouter.Handle("/github/credentials/{id}/", http.HandlerFunc(han.UpdateGithubCredential)).Methods("PUT", "OPTIONS") apiRouter.Handle("/github/credentials/{id}", http.HandlerFunc(han.UpdateGithubCredential)).Methods("PUT", "OPTIONS") + ////////////////////// + // Gitea Endpoints // + ////////////////////// + // Create Gitea Endpoint + apiRouter.Handle("/gitea/endpoints/", http.HandlerFunc(han.CreateGiteaEndpoint)).Methods("POST", "OPTIONS") + apiRouter.Handle("/gitea/endpoints", http.HandlerFunc(han.CreateGiteaEndpoint)).Methods("POST", "OPTIONS") + // List Gitea Endpoints + apiRouter.Handle("/gitea/endpoints/", http.HandlerFunc(han.ListGiteaEndpoints)).Methods("GET", "OPTIONS") + apiRouter.Handle("/gitea/endpoints", http.HandlerFunc(han.ListGiteaEndpoints)).Methods("GET", "OPTIONS") + // Get Gitea Endpoint + apiRouter.Handle("/gitea/endpoints/{name}/", http.HandlerFunc(han.GetGiteaEndpoint)).Methods("GET", "OPTIONS") + apiRouter.Handle("/gitea/endpoints/{name}", http.HandlerFunc(han.GetGiteaEndpoint)).Methods("GET", "OPTIONS") + // Delete Gitea Endpoint + apiRouter.Handle("/gitea/endpoints/{name}/", http.HandlerFunc(han.DeleteGiteaEndpoint)).Methods("DELETE", "OPTIONS") + apiRouter.Handle("/gitea/endpoints/{name}", http.HandlerFunc(han.DeleteGiteaEndpoint)).Methods("DELETE", "OPTIONS") + // Update Gitea Endpoint + apiRouter.Handle("/gitea/endpoints/{name}/", http.HandlerFunc(han.UpdateGiteaEndpoint)).Methods("PUT", "OPTIONS") + apiRouter.Handle("/gitea/endpoints/{name}", http.HandlerFunc(han.UpdateGiteaEndpoint)).Methods("PUT", "OPTIONS") + + //////////////////////// + // Gitea credentials // + //////////////////////// + // List Gitea Credentials + apiRouter.Handle("/gitea/credentials/", http.HandlerFunc(han.ListGiteaCredentials)).Methods("GET", "OPTIONS") + apiRouter.Handle("/gitea/credentials", http.HandlerFunc(han.ListGiteaCredentials)).Methods("GET", "OPTIONS") + // Create Gitea Credentials + apiRouter.Handle("/gitea/credentials/", http.HandlerFunc(han.CreateGiteaCredential)).Methods("POST", "OPTIONS") + apiRouter.Handle("/gitea/credentials", http.HandlerFunc(han.CreateGiteaCredential)).Methods("POST", "OPTIONS") + // Get Gitea Credential + apiRouter.Handle("/gitea/credentials/{id}/", http.HandlerFunc(han.GetGiteaCredential)).Methods("GET", "OPTIONS") + apiRouter.Handle("/gitea/credentials/{id}", http.HandlerFunc(han.GetGiteaCredential)).Methods("GET", "OPTIONS") + // Delete Gitea Credential + apiRouter.Handle("/gitea/credentials/{id}/", http.HandlerFunc(han.DeleteGiteaCredential)).Methods("DELETE", "OPTIONS") + apiRouter.Handle("/gitea/credentials/{id}", http.HandlerFunc(han.DeleteGiteaCredential)).Methods("DELETE", "OPTIONS") + // Update Gitea Credential + apiRouter.Handle("/gitea/credentials/{id}/", http.HandlerFunc(han.UpdateGiteaCredential)).Methods("PUT", "OPTIONS") + apiRouter.Handle("/gitea/credentials/{id}", http.HandlerFunc(han.UpdateGiteaCredential)).Methods("PUT", "OPTIONS") + ///////////////////////// // Websocket endpoints // ///////////////////////// diff --git a/apiserver/swagger-models.yaml b/apiserver/swagger-models.yaml index ad83d6c8..74eaac84 100644 --- a/apiserver/swagger-models.yaml +++ b/apiserver/swagger-models.yaml @@ -74,11 +74,11 @@ definitions: package: github.com/cloudbase/garm/params alias: garm_params items: - $ref: '#/definitions/GithubCredentials' - GithubCredentials: + $ref: '#/definitions/ForgeCredentials' + ForgeCredentials: type: object x-go-type: - type: GithubCredentials + type: ForgeCredentials import: package: github.com/cloudbase/garm/params alias: garm_params @@ -271,22 +271,29 @@ definitions: import: package: github.com/cloudbase/garm/params alias: garm_params - GithubEndpoint: + UpdateGiteaEndpointParams: type: object x-go-type: - type: GithubEndpoint + type: UpdateGiteaEndpointParams import: package: github.com/cloudbase/garm/params alias: garm_params - GithubEndpoints: + ForgeEndpoint: + type: object + x-go-type: + type: ForgeEndpoint + import: + package: github.com/cloudbase/garm/params + alias: garm_params + ForgeEndpoints: type: array x-go-type: - type: GithubEndpoints + type: ForgeEndpoints import: package: github.com/cloudbase/garm/params alias: garm_params items: - $ref: '#/definitions/GithubEndpoint' + $ref: '#/definitions/ForgeEndpoint' CreateGithubEndpointParams: type: object x-go-type: @@ -294,6 +301,13 @@ definitions: import: package: github.com/cloudbase/garm/params alias: garm_params + CreateGiteaEndpointParams: + type: object + x-go-type: + type: CreateGiteaEndpointParams + import: + package: github.com/cloudbase/garm/params + alias: garm_params CreateGithubCredentialsParams: type: object x-go-type: @@ -301,6 +315,13 @@ definitions: import: package: github.com/cloudbase/garm/params alias: garm_params + CreateGiteaCredentialsParams: + type: object + x-go-type: + type: CreateGiteaCredentialsParams + import: + package: github.com/cloudbase/garm/params + alias: garm_params UpdateGithubCredentialsParams: type: object x-go-type: @@ -308,6 +329,13 @@ definitions: import: package: github.com/cloudbase/garm/params alias: garm_params + UpdateGiteaCredentialsParams: + type: object + x-go-type: + type: UpdateGiteaCredentialsParams + import: + package: github.com/cloudbase/garm/params + alias: garm_params UpdateControllerParams: type: object x-go-type: diff --git a/apiserver/swagger.yaml b/apiserver/swagger.yaml index 2f89ab77..66e7a655 100644 --- a/apiserver/swagger.yaml +++ b/apiserver/swagger.yaml @@ -23,6 +23,20 @@ definitions: alias: garm_params package: github.com/cloudbase/garm/params type: CreateEnterpriseParams + CreateGiteaCredentialsParams: + type: object + x-go-type: + import: + alias: garm_params + package: github.com/cloudbase/garm/params + type: CreateGiteaCredentialsParams + CreateGiteaEndpointParams: + type: object + x-go-type: + import: + alias: garm_params + package: github.com/cloudbase/garm/params + type: CreateGiteaEndpointParams CreateGithubCredentialsParams: type: object x-go-type: @@ -74,7 +88,7 @@ definitions: type: CreateScaleSetParams Credentials: items: - $ref: '#/definitions/GithubCredentials' + $ref: '#/definitions/ForgeCredentials' type: array x-go-type: import: @@ -97,29 +111,29 @@ definitions: alias: garm_params package: github.com/cloudbase/garm/params type: Enterprises - GithubCredentials: + ForgeCredentials: type: object x-go-type: import: alias: garm_params package: github.com/cloudbase/garm/params - type: GithubCredentials - GithubEndpoint: + type: ForgeCredentials + ForgeEndpoint: type: object x-go-type: import: alias: garm_params package: github.com/cloudbase/garm/params - type: GithubEndpoint - GithubEndpoints: + type: ForgeEndpoint + ForgeEndpoints: items: - $ref: '#/definitions/GithubEndpoint' + $ref: '#/definitions/ForgeEndpoint' type: array x-go-type: import: alias: garm_params package: github.com/cloudbase/garm/params - type: GithubEndpoints + type: ForgeEndpoints HookInfo: type: object x-go-type: @@ -281,6 +295,20 @@ definitions: alias: garm_params package: github.com/cloudbase/garm/params type: UpdateEntityParams + UpdateGiteaCredentialsParams: + type: object + x-go-type: + import: + alias: garm_params + package: github.com/cloudbase/garm/params + type: UpdateGiteaCredentialsParams + UpdateGiteaEndpointParams: + type: object + x-go-type: + import: + alias: garm_params + package: github.com/cloudbase/garm/params + type: UpdateGiteaEndpointParams UpdateGithubCredentialsParams: type: object x-go-type: @@ -721,6 +749,212 @@ paths: summary: Initialize the first run of the controller. tags: - first-run + /gitea/credentials: + get: + operationId: ListGiteaCredentials + responses: + "200": + description: Credentials + schema: + $ref: '#/definitions/Credentials' + "400": + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: List all credentials. + tags: + - credentials + post: + operationId: CreateGiteaCredentials + parameters: + - description: Parameters used when creating a Gitea credential. + in: body + name: Body + required: true + schema: + $ref: '#/definitions/CreateGiteaCredentialsParams' + description: Parameters used when creating a Gitea credential. + type: object + responses: + "200": + description: ForgeCredentials + schema: + $ref: '#/definitions/ForgeCredentials' + "400": + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Create a Gitea credential. + tags: + - credentials + /gitea/credentials/{id}: + delete: + operationId: DeleteGiteaCredentials + parameters: + - description: ID of the Gitea credential. + in: path + name: id + required: true + type: integer + responses: + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Delete a Gitea credential. + tags: + - credentials + get: + operationId: GetGiteaCredentials + parameters: + - description: ID of the Gitea credential. + in: path + name: id + required: true + type: integer + responses: + "200": + description: ForgeCredentials + schema: + $ref: '#/definitions/ForgeCredentials' + "400": + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Get a Gitea credential. + tags: + - credentials + put: + operationId: UpdateGiteaCredentials + parameters: + - description: ID of the Gitea credential. + in: path + name: id + required: true + type: integer + - description: Parameters used when updating a Gitea credential. + in: body + name: Body + required: true + schema: + $ref: '#/definitions/UpdateGiteaCredentialsParams' + description: Parameters used when updating a Gitea credential. + type: object + responses: + "200": + description: ForgeCredentials + schema: + $ref: '#/definitions/ForgeCredentials' + "400": + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Update a Gitea credential. + tags: + - credentials + /gitea/endpoints: + get: + operationId: ListGiteaEndpoints + responses: + "200": + description: ForgeEndpoints + schema: + $ref: '#/definitions/ForgeEndpoints' + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: List all Gitea Endpoints. + tags: + - endpoints + post: + operationId: CreateGiteaEndpoint + parameters: + - description: Parameters used when creating a Gitea endpoint. + in: body + name: Body + required: true + schema: + $ref: '#/definitions/CreateGiteaEndpointParams' + description: Parameters used when creating a Gitea endpoint. + type: object + responses: + "200": + description: ForgeEndpoint + schema: + $ref: '#/definitions/ForgeEndpoint' + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Create a Gitea Endpoint. + tags: + - endpoints + /gitea/endpoints/{name}: + delete: + operationId: DeleteGiteaEndpoint + parameters: + - description: The name of the Gitea endpoint. + in: path + name: name + required: true + type: string + responses: + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Delete a Gitea Endpoint. + tags: + - endpoints + get: + operationId: GetGiteaEndpoint + parameters: + - description: The name of the Gitea endpoint. + in: path + name: name + required: true + type: string + responses: + "200": + description: ForgeEndpoint + schema: + $ref: '#/definitions/ForgeEndpoint' + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Get a Gitea Endpoint. + tags: + - endpoints + put: + operationId: UpdateGiteaEndpoint + parameters: + - description: The name of the Gitea endpoint. + in: path + name: name + required: true + type: string + - description: Parameters used when updating a Gitea endpoint. + in: body + name: Body + required: true + schema: + $ref: '#/definitions/UpdateGiteaEndpointParams' + description: Parameters used when updating a Gitea endpoint. + type: object + responses: + "200": + description: ForgeEndpoint + schema: + $ref: '#/definitions/ForgeEndpoint' + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Update a Gitea Endpoint. + tags: + - endpoints /github/credentials: get: operationId: ListCredentials @@ -749,9 +983,9 @@ paths: type: object responses: "200": - description: GithubCredentials + description: ForgeCredentials schema: - $ref: '#/definitions/GithubCredentials' + $ref: '#/definitions/ForgeCredentials' "400": description: APIErrorResponse schema: @@ -786,9 +1020,9 @@ paths: type: integer responses: "200": - description: GithubCredentials + description: ForgeCredentials schema: - $ref: '#/definitions/GithubCredentials' + $ref: '#/definitions/ForgeCredentials' "400": description: APIErrorResponse schema: @@ -814,9 +1048,9 @@ paths: type: object responses: "200": - description: GithubCredentials + description: ForgeCredentials schema: - $ref: '#/definitions/GithubCredentials' + $ref: '#/definitions/ForgeCredentials' "400": description: APIErrorResponse schema: @@ -829,9 +1063,9 @@ paths: operationId: ListGithubEndpoints responses: "200": - description: GithubEndpoints + description: ForgeEndpoints schema: - $ref: '#/definitions/GithubEndpoints' + $ref: '#/definitions/ForgeEndpoints' default: description: APIErrorResponse schema: @@ -852,9 +1086,9 @@ paths: type: object responses: "200": - description: GithubEndpoint + description: ForgeEndpoint schema: - $ref: '#/definitions/GithubEndpoint' + $ref: '#/definitions/ForgeEndpoint' default: description: APIErrorResponse schema: @@ -889,9 +1123,9 @@ paths: type: string responses: "200": - description: GithubEndpoint + description: ForgeEndpoint schema: - $ref: '#/definitions/GithubEndpoint' + $ref: '#/definitions/ForgeEndpoint' default: description: APIErrorResponse schema: @@ -917,9 +1151,9 @@ paths: type: object responses: "200": - description: GithubEndpoint + description: ForgeEndpoint schema: - $ref: '#/definitions/GithubEndpoint' + $ref: '#/definitions/ForgeEndpoint' default: description: APIErrorResponse schema: diff --git a/cache/cache_test.go b/cache/cache_test.go index 08b269b8..3e7ed559 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -30,7 +30,7 @@ func (c *CacheTestSuite) TearDownTest() { githubToolsCache.mux.Lock() defer githubToolsCache.mux.Unlock() githubToolsCache.entities = make(map[string]GithubEntityTools) - credentialsCache.cache = make(map[uint]params.GithubCredentials) + credentialsCache.cache = make(map[uint]params.ForgeCredentials) instanceCache.cache = make(map[string]params.Instance) entityCache = &EntityCache{ entities: make(map[string]EntityItem), @@ -90,7 +90,7 @@ func (c *CacheTestSuite) TestGetInexistentCache() { } func (c *CacheTestSuite) TestSetGithubCredentials() { - credentials := params.GithubCredentials{ + credentials := params.ForgeCredentials{ ID: 1, } SetGithubCredentials(credentials) @@ -100,7 +100,7 @@ func (c *CacheTestSuite) TestSetGithubCredentials() { } func (c *CacheTestSuite) TestGetGithubCredentials() { - credentials := params.GithubCredentials{ + credentials := params.ForgeCredentials{ ID: 1, } SetGithubCredentials(credentials) @@ -110,11 +110,11 @@ func (c *CacheTestSuite) TestGetGithubCredentials() { nonExisting, ok := GetGithubCredentials(2) c.Require().False(ok) - c.Require().Equal(params.GithubCredentials{}, nonExisting) + c.Require().Equal(params.ForgeCredentials{}, nonExisting) } func (c *CacheTestSuite) TestDeleteGithubCredentials() { - credentials := params.GithubCredentials{ + credentials := params.ForgeCredentials{ ID: 1, } SetGithubCredentials(credentials) @@ -125,14 +125,14 @@ func (c *CacheTestSuite) TestDeleteGithubCredentials() { DeleteGithubCredentials(1) cachedCreds, ok = GetGithubCredentials(1) c.Require().False(ok) - c.Require().Equal(params.GithubCredentials{}, cachedCreds) + c.Require().Equal(params.ForgeCredentials{}, cachedCreds) } func (c *CacheTestSuite) TestGetAllGithubCredentials() { - credentials1 := params.GithubCredentials{ + credentials1 := params.ForgeCredentials{ ID: 1, } - credentials2 := params.GithubCredentials{ + credentials2 := params.ForgeCredentials{ ID: 2, } SetGithubCredentials(credentials1) @@ -265,12 +265,12 @@ func (c *CacheTestSuite) TestSetGetEntityCache() { c.Require().True(ok) c.Require().Equal(entity.ID, cachedEntity.ID) - entity.Credentials.GithubCredentials.Description = "test description" + entity.Credentials.Description = "test description" SetEntity(entity) cachedEntity, ok = GetEntity("test-entity") c.Require().True(ok) c.Require().Equal(entity.ID, cachedEntity.ID) - c.Require().Equal(entity.Credentials.GithubCredentials.Description, cachedEntity.Credentials.GithubCredentials.Description) + c.Require().Equal(entity.Credentials.Description, cachedEntity.Credentials.Description) } func (c *CacheTestSuite) TestReplaceEntityPools() { @@ -280,10 +280,7 @@ func (c *CacheTestSuite) TestReplaceEntityPools() { Name: "test", Owner: "test", Credentials: params.ForgeCredentials{ - ForgeType: params.GithubEndpointType, - GithubCredentials: params.GithubCredentials{ - ID: 1, - }, + ID: 1, }, } pool1 := params.Pool{ @@ -293,7 +290,7 @@ func (c *CacheTestSuite) TestReplaceEntityPools() { ID: "pool-2", } - credentials := params.GithubCredentials{ + credentials := params.ForgeCredentials{ ID: 1, Name: "test", } @@ -304,7 +301,7 @@ func (c *CacheTestSuite) TestReplaceEntityPools() { cachedEntity, ok := GetEntity(entity.ID) c.Require().True(ok) c.Require().Equal(entity.ID, cachedEntity.ID) - c.Require().Equal("test", cachedEntity.Credentials.GithubCredentials.Name) + c.Require().Equal("test", cachedEntity.Credentials.Name) pools := GetEntityPools(entity.ID) c.Require().Len(pools, 2) diff --git a/cache/credentials_cache.go b/cache/credentials_cache.go index 060b076a..d5626f40 100644 --- a/cache/credentials_cache.go +++ b/cache/credentials_cache.go @@ -6,61 +6,67 @@ import ( "github.com/cloudbase/garm/params" ) -var credentialsCache *GithubCredentials +var credentialsCache *CredentialCache +var giteaCredentialsCache *CredentialCache func init() { - ghCredentialsCache := &GithubCredentials{ - cache: make(map[uint]params.GithubCredentials), + ghCredentialsCache := &CredentialCache{ + cache: make(map[uint]params.ForgeCredentials), } + gtCredentialsCache := &CredentialCache{ + cache: make(map[uint]params.ForgeCredentials), + } + credentialsCache = ghCredentialsCache + giteaCredentialsCache = gtCredentialsCache } -type GithubCredentials struct { +type CredentialCache struct { mux sync.Mutex - cache map[uint]params.GithubCredentials + cache map[uint]params.ForgeCredentials } -func (g *GithubCredentials) SetCredentialsRateLimit(credsID uint, rateLimit params.GithubRateLimit) { +func (g *CredentialCache) SetCredentialsRateLimit(credsID uint, rateLimit params.GithubRateLimit) { g.mux.Lock() defer g.mux.Unlock() if creds, ok := g.cache[credsID]; ok { - creds.RateLimit = rateLimit + creds.RateLimit = &rateLimit g.cache[credsID] = creds } } -func (g *GithubCredentials) SetCredentials(credentials params.GithubCredentials) { +func (g *CredentialCache) SetCredentials(credentials params.ForgeCredentials) { g.mux.Lock() defer g.mux.Unlock() g.cache[credentials.ID] = credentials - UpdateCredentialsInAffectedEntities(credentials.GetForgeCredentials()) + UpdateCredentialsInAffectedEntities(credentials) } -func (g *GithubCredentials) GetCredentials(id uint) (params.GithubCredentials, bool) { +func (g *CredentialCache) GetCredentials(id uint) (params.ForgeCredentials, bool) { g.mux.Lock() defer g.mux.Unlock() if creds, ok := g.cache[id]; ok { return creds, true } - return params.GithubCredentials{}, false + return params.ForgeCredentials{}, false } -func (g *GithubCredentials) DeleteCredentials(id uint) { +func (g *CredentialCache) DeleteCredentials(id uint) { g.mux.Lock() defer g.mux.Unlock() delete(g.cache, id) } -func (g *GithubCredentials) GetAllCredentials() []params.GithubCredentials { +func (g *CredentialCache) GetAllCredentials() []params.ForgeCredentials { g.mux.Lock() defer g.mux.Unlock() - creds := make([]params.GithubCredentials, 0, len(g.cache)) + creds := make([]params.ForgeCredentials, 0, len(g.cache)) for _, cred := range g.cache { creds = append(creds, cred) } @@ -70,11 +76,11 @@ func (g *GithubCredentials) GetAllCredentials() []params.GithubCredentials { return creds } -func (g *GithubCredentials) GetAllCredentialsAsMap() map[uint]params.GithubCredentials { +func (g *CredentialCache) GetAllCredentialsAsMap() map[uint]params.ForgeCredentials { g.mux.Lock() defer g.mux.Unlock() - creds := make(map[uint]params.GithubCredentials, len(g.cache)) + creds := make(map[uint]params.ForgeCredentials, len(g.cache)) for id, cred := range g.cache { creds[id] = cred } @@ -82,11 +88,11 @@ func (g *GithubCredentials) GetAllCredentialsAsMap() map[uint]params.GithubCrede return creds } -func SetGithubCredentials(credentials params.GithubCredentials) { +func SetGithubCredentials(credentials params.ForgeCredentials) { credentialsCache.SetCredentials(credentials) } -func GetGithubCredentials(id uint) (params.GithubCredentials, bool) { +func GetGithubCredentials(id uint) (params.ForgeCredentials, bool) { return credentialsCache.GetCredentials(id) } @@ -94,7 +100,7 @@ func DeleteGithubCredentials(id uint) { credentialsCache.DeleteCredentials(id) } -func GetAllGithubCredentials() []params.GithubCredentials { +func GetAllGithubCredentials() []params.ForgeCredentials { return credentialsCache.GetAllCredentials() } @@ -102,6 +108,26 @@ func SetCredentialsRateLimit(credsID uint, rateLimit params.GithubRateLimit) { credentialsCache.SetCredentialsRateLimit(credsID, rateLimit) } -func GetAllGithubCredentialsAsMap() map[uint]params.GithubCredentials { +func GetAllGithubCredentialsAsMap() map[uint]params.ForgeCredentials { return credentialsCache.GetAllCredentialsAsMap() } + +func SetGiteaCredentials(credentials params.ForgeCredentials) { + giteaCredentialsCache.SetCredentials(credentials) +} + +func GetGiteaCredentials(id uint) (params.ForgeCredentials, bool) { + return giteaCredentialsCache.GetCredentials(id) +} + +func DeleteGiteaCredentials(id uint) { + giteaCredentialsCache.DeleteCredentials(id) +} + +func GetAllGiteaCredentials() []params.ForgeCredentials { + return giteaCredentialsCache.GetAllCredentials() +} + +func GetAllGiteaCredentialsAsMap() map[uint]params.ForgeCredentials { + return giteaCredentialsCache.GetAllCredentialsAsMap() +} diff --git a/cache/entity_cache.go b/cache/entity_cache.go index 74f406de..6bd1f2c7 100644 --- a/cache/entity_cache.go +++ b/cache/entity_cache.go @@ -44,18 +44,16 @@ func (e *EntityCache) GetEntity(entityID string) (params.ForgeEntity, bool) { defer e.mux.Unlock() if cache, ok := e.entities[entityID]; ok { - // Get the credentials from the credentials cache. - var forgeCredsGetter params.ForgeCredentialsGetter - var credsOk bool + var creds params.ForgeCredentials + var ok bool switch cache.Entity.Credentials.ForgeType { case params.GithubEndpointType: - forgeCredsGetter, credsOk = GetGithubCredentials(cache.Entity.Credentials.GetID()) + creds, ok = GetGithubCredentials(cache.Entity.Credentials.ID) case params.GiteaEndpointType: - // add gitea credentials getter - return cache.Entity, false + creds, ok = GetGiteaCredentials(cache.Entity.Credentials.ID) } - if credsOk { - cache.Entity.Credentials = forgeCredsGetter.GetForgeCredentials() + if ok { + cache.Entity.Credentials = creds } return cache.Entity, true } @@ -254,17 +252,16 @@ func (e *EntityCache) GetAllEntities() []params.ForgeEntity { var entities []params.ForgeEntity for _, cache := range e.entities { // Get the credentials from the credentials cache. - var forgeCredsGetter params.ForgeCredentialsGetter - var credsOk bool + var creds params.ForgeCredentials + var ok bool switch cache.Entity.Credentials.ForgeType { case params.GithubEndpointType: - forgeCredsGetter, credsOk = GetGithubCredentials(cache.Entity.Credentials.GetID()) + creds, ok = GetGithubCredentials(cache.Entity.Credentials.ID) case params.GiteaEndpointType: - // add gitea credentials getter - return nil + creds, ok = GetGiteaCredentials(cache.Entity.Credentials.ID) } - if credsOk { - cache.Entity.Credentials = forgeCredsGetter.GetForgeCredentials() + if ok { + cache.Entity.Credentials = creds } entities = append(entities, cache.Entity) } diff --git a/cache/tools_cache.go b/cache/tools_cache.go index f4a2db62..0698c41e 100644 --- a/cache/tools_cache.go +++ b/cache/tools_cache.go @@ -19,6 +19,7 @@ func init() { type GithubEntityTools struct { updatedAt time.Time + expiresAt time.Time entity params.ForgeEntity tools []commonParams.RunnerApplicationDownload } @@ -34,10 +35,12 @@ func (g *GithubToolsCache) Get(entityID string) ([]commonParams.RunnerApplicatio defer g.mux.Unlock() if cache, ok := g.entities[entityID]; ok { - if time.Since(cache.updatedAt) > 1*time.Hour { - // Stale cache, remove it. - delete(g.entities, entityID) - return nil, false + if cache.entity.Credentials.ForgeType == params.GithubEndpointType { + if time.Now().UTC().After(cache.expiresAt.Add(-5 * time.Minute)) { + // Stale cache, remove it. + delete(g.entities, entityID) + return nil, false + } } return cache.tools, true } @@ -48,11 +51,17 @@ func (g *GithubToolsCache) Set(entity params.ForgeEntity, tools []commonParams.R g.mux.Lock() defer g.mux.Unlock() - g.entities[entity.ID] = GithubEntityTools{ + forgeTools := GithubEntityTools{ updatedAt: time.Now(), entity: entity, tools: tools, } + + if entity.Credentials.ForgeType == params.GithubEndpointType { + forgeTools.expiresAt = time.Now().Add(24 * time.Hour) + } + + g.entities[entity.ID] = forgeTools } func SetGithubToolsCache(entity params.ForgeEntity, tools []commonParams.RunnerApplicationDownload) { diff --git a/client/credentials/create_credentials_responses.go b/client/credentials/create_credentials_responses.go index cc5dc5dc..a0037edf 100644 --- a/client/credentials/create_credentials_responses.go +++ b/client/credentials/create_credentials_responses.go @@ -50,10 +50,10 @@ func NewCreateCredentialsOK() *CreateCredentialsOK { /* CreateCredentialsOK describes a response with status code 200, with default header values. -GithubCredentials +ForgeCredentials */ type CreateCredentialsOK struct { - Payload garm_params.GithubCredentials + Payload garm_params.ForgeCredentials } // IsSuccess returns true when this create credentials o k response has a 2xx status code @@ -96,7 +96,7 @@ func (o *CreateCredentialsOK) String() string { return fmt.Sprintf("[POST /github/credentials][%d] createCredentialsOK %s", 200, payload) } -func (o *CreateCredentialsOK) GetPayload() garm_params.GithubCredentials { +func (o *CreateCredentialsOK) GetPayload() garm_params.ForgeCredentials { return o.Payload } diff --git a/client/credentials/create_gitea_credentials_parameters.go b/client/credentials/create_gitea_credentials_parameters.go new file mode 100644 index 00000000..6e255bfa --- /dev/null +++ b/client/credentials/create_gitea_credentials_parameters.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + garm_params "github.com/cloudbase/garm/params" +) + +// NewCreateGiteaCredentialsParams creates a new CreateGiteaCredentialsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewCreateGiteaCredentialsParams() *CreateGiteaCredentialsParams { + return &CreateGiteaCredentialsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewCreateGiteaCredentialsParamsWithTimeout creates a new CreateGiteaCredentialsParams object +// with the ability to set a timeout on a request. +func NewCreateGiteaCredentialsParamsWithTimeout(timeout time.Duration) *CreateGiteaCredentialsParams { + return &CreateGiteaCredentialsParams{ + timeout: timeout, + } +} + +// NewCreateGiteaCredentialsParamsWithContext creates a new CreateGiteaCredentialsParams object +// with the ability to set a context for a request. +func NewCreateGiteaCredentialsParamsWithContext(ctx context.Context) *CreateGiteaCredentialsParams { + return &CreateGiteaCredentialsParams{ + Context: ctx, + } +} + +// NewCreateGiteaCredentialsParamsWithHTTPClient creates a new CreateGiteaCredentialsParams object +// with the ability to set a custom HTTPClient for a request. +func NewCreateGiteaCredentialsParamsWithHTTPClient(client *http.Client) *CreateGiteaCredentialsParams { + return &CreateGiteaCredentialsParams{ + HTTPClient: client, + } +} + +/* +CreateGiteaCredentialsParams contains all the parameters to send to the API endpoint + + for the create gitea credentials operation. + + Typically these are written to a http.Request. +*/ +type CreateGiteaCredentialsParams struct { + + /* Body. + + Parameters used when creating a Gitea credential. + */ + Body garm_params.CreateGiteaCredentialsParams + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the create gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *CreateGiteaCredentialsParams) WithDefaults() *CreateGiteaCredentialsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the create gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *CreateGiteaCredentialsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the create gitea credentials params +func (o *CreateGiteaCredentialsParams) WithTimeout(timeout time.Duration) *CreateGiteaCredentialsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the create gitea credentials params +func (o *CreateGiteaCredentialsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the create gitea credentials params +func (o *CreateGiteaCredentialsParams) WithContext(ctx context.Context) *CreateGiteaCredentialsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the create gitea credentials params +func (o *CreateGiteaCredentialsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the create gitea credentials params +func (o *CreateGiteaCredentialsParams) WithHTTPClient(client *http.Client) *CreateGiteaCredentialsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the create gitea credentials params +func (o *CreateGiteaCredentialsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the create gitea credentials params +func (o *CreateGiteaCredentialsParams) WithBody(body garm_params.CreateGiteaCredentialsParams) *CreateGiteaCredentialsParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the create gitea credentials params +func (o *CreateGiteaCredentialsParams) SetBody(body garm_params.CreateGiteaCredentialsParams) { + o.Body = body +} + +// WriteToRequest writes these params to a swagger request +func (o *CreateGiteaCredentialsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/credentials/create_gitea_credentials_responses.go b/client/credentials/create_gitea_credentials_responses.go new file mode 100644 index 00000000..2389cb04 --- /dev/null +++ b/client/credentials/create_gitea_credentials_responses.go @@ -0,0 +1,179 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// CreateGiteaCredentialsReader is a Reader for the CreateGiteaCredentials structure. +type CreateGiteaCredentialsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *CreateGiteaCredentialsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewCreateGiteaCredentialsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewCreateGiteaCredentialsBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("[POST /gitea/credentials] CreateGiteaCredentials", response, response.Code()) + } +} + +// NewCreateGiteaCredentialsOK creates a CreateGiteaCredentialsOK with default headers values +func NewCreateGiteaCredentialsOK() *CreateGiteaCredentialsOK { + return &CreateGiteaCredentialsOK{} +} + +/* +CreateGiteaCredentialsOK describes a response with status code 200, with default header values. + +ForgeCredentials +*/ +type CreateGiteaCredentialsOK struct { + Payload garm_params.ForgeCredentials +} + +// IsSuccess returns true when this create gitea credentials o k response has a 2xx status code +func (o *CreateGiteaCredentialsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this create gitea credentials o k response has a 3xx status code +func (o *CreateGiteaCredentialsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this create gitea credentials o k response has a 4xx status code +func (o *CreateGiteaCredentialsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this create gitea credentials o k response has a 5xx status code +func (o *CreateGiteaCredentialsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this create gitea credentials o k response a status code equal to that given +func (o *CreateGiteaCredentialsOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the create gitea credentials o k response +func (o *CreateGiteaCredentialsOK) Code() int { + return 200 +} + +func (o *CreateGiteaCredentialsOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[POST /gitea/credentials][%d] createGiteaCredentialsOK %s", 200, payload) +} + +func (o *CreateGiteaCredentialsOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[POST /gitea/credentials][%d] createGiteaCredentialsOK %s", 200, payload) +} + +func (o *CreateGiteaCredentialsOK) GetPayload() garm_params.ForgeCredentials { + return o.Payload +} + +func (o *CreateGiteaCredentialsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewCreateGiteaCredentialsBadRequest creates a CreateGiteaCredentialsBadRequest with default headers values +func NewCreateGiteaCredentialsBadRequest() *CreateGiteaCredentialsBadRequest { + return &CreateGiteaCredentialsBadRequest{} +} + +/* +CreateGiteaCredentialsBadRequest describes a response with status code 400, with default header values. + +APIErrorResponse +*/ +type CreateGiteaCredentialsBadRequest struct { + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this create gitea credentials bad request response has a 2xx status code +func (o *CreateGiteaCredentialsBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this create gitea credentials bad request response has a 3xx status code +func (o *CreateGiteaCredentialsBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this create gitea credentials bad request response has a 4xx status code +func (o *CreateGiteaCredentialsBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this create gitea credentials bad request response has a 5xx status code +func (o *CreateGiteaCredentialsBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this create gitea credentials bad request response a status code equal to that given +func (o *CreateGiteaCredentialsBadRequest) IsCode(code int) bool { + return code == 400 +} + +// Code gets the status code for the create gitea credentials bad request response +func (o *CreateGiteaCredentialsBadRequest) Code() int { + return 400 +} + +func (o *CreateGiteaCredentialsBadRequest) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[POST /gitea/credentials][%d] createGiteaCredentialsBadRequest %s", 400, payload) +} + +func (o *CreateGiteaCredentialsBadRequest) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[POST /gitea/credentials][%d] createGiteaCredentialsBadRequest %s", 400, payload) +} + +func (o *CreateGiteaCredentialsBadRequest) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *CreateGiteaCredentialsBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/credentials/credentials_client.go b/client/credentials/credentials_client.go index 9d7b0563..3dfe1abd 100644 --- a/client/credentials/credentials_client.go +++ b/client/credentials/credentials_client.go @@ -58,14 +58,24 @@ type ClientOption func(*runtime.ClientOperation) type ClientService interface { CreateCredentials(params *CreateCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*CreateCredentialsOK, error) + CreateGiteaCredentials(params *CreateGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*CreateGiteaCredentialsOK, error) + DeleteCredentials(params *DeleteCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) error + DeleteGiteaCredentials(params *DeleteGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) error + GetCredentials(params *GetCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetCredentialsOK, error) + GetGiteaCredentials(params *GetGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetGiteaCredentialsOK, error) + ListCredentials(params *ListCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListCredentialsOK, error) + ListGiteaCredentials(params *ListGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListGiteaCredentialsOK, error) + UpdateCredentials(params *UpdateCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*UpdateCredentialsOK, error) + UpdateGiteaCredentials(params *UpdateGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*UpdateGiteaCredentialsOK, error) + SetTransport(transport runtime.ClientTransport) } @@ -108,6 +118,45 @@ func (a *Client) CreateCredentials(params *CreateCredentialsParams, authInfo run panic(msg) } +/* +CreateGiteaCredentials creates a gitea credential +*/ +func (a *Client) CreateGiteaCredentials(params *CreateGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*CreateGiteaCredentialsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewCreateGiteaCredentialsParams() + } + op := &runtime.ClientOperation{ + ID: "CreateGiteaCredentials", + Method: "POST", + PathPattern: "/gitea/credentials", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &CreateGiteaCredentialsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*CreateGiteaCredentialsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for CreateGiteaCredentials: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* DeleteCredentials deletes a git hub credential */ @@ -140,6 +189,38 @@ func (a *Client) DeleteCredentials(params *DeleteCredentialsParams, authInfo run return nil } +/* +DeleteGiteaCredentials deletes a gitea credential +*/ +func (a *Client) DeleteGiteaCredentials(params *DeleteGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) error { + // TODO: Validate the params before sending + if params == nil { + params = NewDeleteGiteaCredentialsParams() + } + op := &runtime.ClientOperation{ + ID: "DeleteGiteaCredentials", + Method: "DELETE", + PathPattern: "/gitea/credentials/{id}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &DeleteGiteaCredentialsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + _, err := a.transport.Submit(op) + if err != nil { + return err + } + return nil +} + /* GetCredentials gets a git hub credential */ @@ -179,6 +260,45 @@ func (a *Client) GetCredentials(params *GetCredentialsParams, authInfo runtime.C panic(msg) } +/* +GetGiteaCredentials gets a gitea credential +*/ +func (a *Client) GetGiteaCredentials(params *GetGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetGiteaCredentialsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetGiteaCredentialsParams() + } + op := &runtime.ClientOperation{ + ID: "GetGiteaCredentials", + Method: "GET", + PathPattern: "/gitea/credentials/{id}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetGiteaCredentialsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetGiteaCredentialsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for GetGiteaCredentials: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* ListCredentials lists all credentials */ @@ -218,6 +338,45 @@ func (a *Client) ListCredentials(params *ListCredentialsParams, authInfo runtime panic(msg) } +/* +ListGiteaCredentials lists all credentials +*/ +func (a *Client) ListGiteaCredentials(params *ListGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListGiteaCredentialsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewListGiteaCredentialsParams() + } + op := &runtime.ClientOperation{ + ID: "ListGiteaCredentials", + Method: "GET", + PathPattern: "/gitea/credentials", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &ListGiteaCredentialsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*ListGiteaCredentialsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for ListGiteaCredentials: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* UpdateCredentials updates a git hub credential */ @@ -257,6 +416,45 @@ func (a *Client) UpdateCredentials(params *UpdateCredentialsParams, authInfo run panic(msg) } +/* +UpdateGiteaCredentials updates a gitea credential +*/ +func (a *Client) UpdateGiteaCredentials(params *UpdateGiteaCredentialsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*UpdateGiteaCredentialsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewUpdateGiteaCredentialsParams() + } + op := &runtime.ClientOperation{ + ID: "UpdateGiteaCredentials", + Method: "PUT", + PathPattern: "/gitea/credentials/{id}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &UpdateGiteaCredentialsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*UpdateGiteaCredentialsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for UpdateGiteaCredentials: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + // SetTransport changes the transport on the client func (a *Client) SetTransport(transport runtime.ClientTransport) { a.transport = transport diff --git a/client/credentials/delete_gitea_credentials_parameters.go b/client/credentials/delete_gitea_credentials_parameters.go new file mode 100644 index 00000000..598ac477 --- /dev/null +++ b/client/credentials/delete_gitea_credentials_parameters.go @@ -0,0 +1,152 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NewDeleteGiteaCredentialsParams creates a new DeleteGiteaCredentialsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewDeleteGiteaCredentialsParams() *DeleteGiteaCredentialsParams { + return &DeleteGiteaCredentialsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewDeleteGiteaCredentialsParamsWithTimeout creates a new DeleteGiteaCredentialsParams object +// with the ability to set a timeout on a request. +func NewDeleteGiteaCredentialsParamsWithTimeout(timeout time.Duration) *DeleteGiteaCredentialsParams { + return &DeleteGiteaCredentialsParams{ + timeout: timeout, + } +} + +// NewDeleteGiteaCredentialsParamsWithContext creates a new DeleteGiteaCredentialsParams object +// with the ability to set a context for a request. +func NewDeleteGiteaCredentialsParamsWithContext(ctx context.Context) *DeleteGiteaCredentialsParams { + return &DeleteGiteaCredentialsParams{ + Context: ctx, + } +} + +// NewDeleteGiteaCredentialsParamsWithHTTPClient creates a new DeleteGiteaCredentialsParams object +// with the ability to set a custom HTTPClient for a request. +func NewDeleteGiteaCredentialsParamsWithHTTPClient(client *http.Client) *DeleteGiteaCredentialsParams { + return &DeleteGiteaCredentialsParams{ + HTTPClient: client, + } +} + +/* +DeleteGiteaCredentialsParams contains all the parameters to send to the API endpoint + + for the delete gitea credentials operation. + + Typically these are written to a http.Request. +*/ +type DeleteGiteaCredentialsParams struct { + + /* ID. + + ID of the Gitea credential. + */ + ID int64 + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the delete gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteGiteaCredentialsParams) WithDefaults() *DeleteGiteaCredentialsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the delete gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteGiteaCredentialsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the delete gitea credentials params +func (o *DeleteGiteaCredentialsParams) WithTimeout(timeout time.Duration) *DeleteGiteaCredentialsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the delete gitea credentials params +func (o *DeleteGiteaCredentialsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the delete gitea credentials params +func (o *DeleteGiteaCredentialsParams) WithContext(ctx context.Context) *DeleteGiteaCredentialsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the delete gitea credentials params +func (o *DeleteGiteaCredentialsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the delete gitea credentials params +func (o *DeleteGiteaCredentialsParams) WithHTTPClient(client *http.Client) *DeleteGiteaCredentialsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the delete gitea credentials params +func (o *DeleteGiteaCredentialsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithID adds the id to the delete gitea credentials params +func (o *DeleteGiteaCredentialsParams) WithID(id int64) *DeleteGiteaCredentialsParams { + o.SetID(id) + return o +} + +// SetID adds the id to the delete gitea credentials params +func (o *DeleteGiteaCredentialsParams) SetID(id int64) { + o.ID = id +} + +// WriteToRequest writes these params to a swagger request +func (o *DeleteGiteaCredentialsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param id + if err := r.SetPathParam("id", swag.FormatInt64(o.ID)); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/credentials/delete_gitea_credentials_responses.go b/client/credentials/delete_gitea_credentials_responses.go new file mode 100644 index 00000000..d1df7b0b --- /dev/null +++ b/client/credentials/delete_gitea_credentials_responses.go @@ -0,0 +1,106 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" +) + +// DeleteGiteaCredentialsReader is a Reader for the DeleteGiteaCredentials structure. +type DeleteGiteaCredentialsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *DeleteGiteaCredentialsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + result := NewDeleteGiteaCredentialsDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result +} + +// NewDeleteGiteaCredentialsDefault creates a DeleteGiteaCredentialsDefault with default headers values +func NewDeleteGiteaCredentialsDefault(code int) *DeleteGiteaCredentialsDefault { + return &DeleteGiteaCredentialsDefault{ + _statusCode: code, + } +} + +/* +DeleteGiteaCredentialsDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type DeleteGiteaCredentialsDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this delete gitea credentials default response has a 2xx status code +func (o *DeleteGiteaCredentialsDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this delete gitea credentials default response has a 3xx status code +func (o *DeleteGiteaCredentialsDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this delete gitea credentials default response has a 4xx status code +func (o *DeleteGiteaCredentialsDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this delete gitea credentials default response has a 5xx status code +func (o *DeleteGiteaCredentialsDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this delete gitea credentials default response a status code equal to that given +func (o *DeleteGiteaCredentialsDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the delete gitea credentials default response +func (o *DeleteGiteaCredentialsDefault) Code() int { + return o._statusCode +} + +func (o *DeleteGiteaCredentialsDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[DELETE /gitea/credentials/{id}][%d] DeleteGiteaCredentials default %s", o._statusCode, payload) +} + +func (o *DeleteGiteaCredentialsDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[DELETE /gitea/credentials/{id}][%d] DeleteGiteaCredentials default %s", o._statusCode, payload) +} + +func (o *DeleteGiteaCredentialsDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *DeleteGiteaCredentialsDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/credentials/get_credentials_responses.go b/client/credentials/get_credentials_responses.go index 1c2b800b..4538c16e 100644 --- a/client/credentials/get_credentials_responses.go +++ b/client/credentials/get_credentials_responses.go @@ -50,10 +50,10 @@ func NewGetCredentialsOK() *GetCredentialsOK { /* GetCredentialsOK describes a response with status code 200, with default header values. -GithubCredentials +ForgeCredentials */ type GetCredentialsOK struct { - Payload garm_params.GithubCredentials + Payload garm_params.ForgeCredentials } // IsSuccess returns true when this get credentials o k response has a 2xx status code @@ -96,7 +96,7 @@ func (o *GetCredentialsOK) String() string { return fmt.Sprintf("[GET /github/credentials/{id}][%d] getCredentialsOK %s", 200, payload) } -func (o *GetCredentialsOK) GetPayload() garm_params.GithubCredentials { +func (o *GetCredentialsOK) GetPayload() garm_params.ForgeCredentials { return o.Payload } diff --git a/client/credentials/get_gitea_credentials_parameters.go b/client/credentials/get_gitea_credentials_parameters.go new file mode 100644 index 00000000..a844c326 --- /dev/null +++ b/client/credentials/get_gitea_credentials_parameters.go @@ -0,0 +1,152 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NewGetGiteaCredentialsParams creates a new GetGiteaCredentialsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetGiteaCredentialsParams() *GetGiteaCredentialsParams { + return &GetGiteaCredentialsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetGiteaCredentialsParamsWithTimeout creates a new GetGiteaCredentialsParams object +// with the ability to set a timeout on a request. +func NewGetGiteaCredentialsParamsWithTimeout(timeout time.Duration) *GetGiteaCredentialsParams { + return &GetGiteaCredentialsParams{ + timeout: timeout, + } +} + +// NewGetGiteaCredentialsParamsWithContext creates a new GetGiteaCredentialsParams object +// with the ability to set a context for a request. +func NewGetGiteaCredentialsParamsWithContext(ctx context.Context) *GetGiteaCredentialsParams { + return &GetGiteaCredentialsParams{ + Context: ctx, + } +} + +// NewGetGiteaCredentialsParamsWithHTTPClient creates a new GetGiteaCredentialsParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetGiteaCredentialsParamsWithHTTPClient(client *http.Client) *GetGiteaCredentialsParams { + return &GetGiteaCredentialsParams{ + HTTPClient: client, + } +} + +/* +GetGiteaCredentialsParams contains all the parameters to send to the API endpoint + + for the get gitea credentials operation. + + Typically these are written to a http.Request. +*/ +type GetGiteaCredentialsParams struct { + + /* ID. + + ID of the Gitea credential. + */ + ID int64 + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetGiteaCredentialsParams) WithDefaults() *GetGiteaCredentialsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetGiteaCredentialsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get gitea credentials params +func (o *GetGiteaCredentialsParams) WithTimeout(timeout time.Duration) *GetGiteaCredentialsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get gitea credentials params +func (o *GetGiteaCredentialsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get gitea credentials params +func (o *GetGiteaCredentialsParams) WithContext(ctx context.Context) *GetGiteaCredentialsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get gitea credentials params +func (o *GetGiteaCredentialsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get gitea credentials params +func (o *GetGiteaCredentialsParams) WithHTTPClient(client *http.Client) *GetGiteaCredentialsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get gitea credentials params +func (o *GetGiteaCredentialsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithID adds the id to the get gitea credentials params +func (o *GetGiteaCredentialsParams) WithID(id int64) *GetGiteaCredentialsParams { + o.SetID(id) + return o +} + +// SetID adds the id to the get gitea credentials params +func (o *GetGiteaCredentialsParams) SetID(id int64) { + o.ID = id +} + +// WriteToRequest writes these params to a swagger request +func (o *GetGiteaCredentialsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param id + if err := r.SetPathParam("id", swag.FormatInt64(o.ID)); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/credentials/get_gitea_credentials_responses.go b/client/credentials/get_gitea_credentials_responses.go new file mode 100644 index 00000000..ba116d63 --- /dev/null +++ b/client/credentials/get_gitea_credentials_responses.go @@ -0,0 +1,179 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// GetGiteaCredentialsReader is a Reader for the GetGiteaCredentials structure. +type GetGiteaCredentialsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetGiteaCredentialsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetGiteaCredentialsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewGetGiteaCredentialsBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("[GET /gitea/credentials/{id}] GetGiteaCredentials", response, response.Code()) + } +} + +// NewGetGiteaCredentialsOK creates a GetGiteaCredentialsOK with default headers values +func NewGetGiteaCredentialsOK() *GetGiteaCredentialsOK { + return &GetGiteaCredentialsOK{} +} + +/* +GetGiteaCredentialsOK describes a response with status code 200, with default header values. + +ForgeCredentials +*/ +type GetGiteaCredentialsOK struct { + Payload garm_params.ForgeCredentials +} + +// IsSuccess returns true when this get gitea credentials o k response has a 2xx status code +func (o *GetGiteaCredentialsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get gitea credentials o k response has a 3xx status code +func (o *GetGiteaCredentialsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get gitea credentials o k response has a 4xx status code +func (o *GetGiteaCredentialsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get gitea credentials o k response has a 5xx status code +func (o *GetGiteaCredentialsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get gitea credentials o k response a status code equal to that given +func (o *GetGiteaCredentialsOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the get gitea credentials o k response +func (o *GetGiteaCredentialsOK) Code() int { + return 200 +} + +func (o *GetGiteaCredentialsOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/credentials/{id}][%d] getGiteaCredentialsOK %s", 200, payload) +} + +func (o *GetGiteaCredentialsOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/credentials/{id}][%d] getGiteaCredentialsOK %s", 200, payload) +} + +func (o *GetGiteaCredentialsOK) GetPayload() garm_params.ForgeCredentials { + return o.Payload +} + +func (o *GetGiteaCredentialsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetGiteaCredentialsBadRequest creates a GetGiteaCredentialsBadRequest with default headers values +func NewGetGiteaCredentialsBadRequest() *GetGiteaCredentialsBadRequest { + return &GetGiteaCredentialsBadRequest{} +} + +/* +GetGiteaCredentialsBadRequest describes a response with status code 400, with default header values. + +APIErrorResponse +*/ +type GetGiteaCredentialsBadRequest struct { + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this get gitea credentials bad request response has a 2xx status code +func (o *GetGiteaCredentialsBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get gitea credentials bad request response has a 3xx status code +func (o *GetGiteaCredentialsBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get gitea credentials bad request response has a 4xx status code +func (o *GetGiteaCredentialsBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this get gitea credentials bad request response has a 5xx status code +func (o *GetGiteaCredentialsBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this get gitea credentials bad request response a status code equal to that given +func (o *GetGiteaCredentialsBadRequest) IsCode(code int) bool { + return code == 400 +} + +// Code gets the status code for the get gitea credentials bad request response +func (o *GetGiteaCredentialsBadRequest) Code() int { + return 400 +} + +func (o *GetGiteaCredentialsBadRequest) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/credentials/{id}][%d] getGiteaCredentialsBadRequest %s", 400, payload) +} + +func (o *GetGiteaCredentialsBadRequest) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/credentials/{id}][%d] getGiteaCredentialsBadRequest %s", 400, payload) +} + +func (o *GetGiteaCredentialsBadRequest) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *GetGiteaCredentialsBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/credentials/list_gitea_credentials_parameters.go b/client/credentials/list_gitea_credentials_parameters.go new file mode 100644 index 00000000..5e321a88 --- /dev/null +++ b/client/credentials/list_gitea_credentials_parameters.go @@ -0,0 +1,128 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewListGiteaCredentialsParams creates a new ListGiteaCredentialsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewListGiteaCredentialsParams() *ListGiteaCredentialsParams { + return &ListGiteaCredentialsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewListGiteaCredentialsParamsWithTimeout creates a new ListGiteaCredentialsParams object +// with the ability to set a timeout on a request. +func NewListGiteaCredentialsParamsWithTimeout(timeout time.Duration) *ListGiteaCredentialsParams { + return &ListGiteaCredentialsParams{ + timeout: timeout, + } +} + +// NewListGiteaCredentialsParamsWithContext creates a new ListGiteaCredentialsParams object +// with the ability to set a context for a request. +func NewListGiteaCredentialsParamsWithContext(ctx context.Context) *ListGiteaCredentialsParams { + return &ListGiteaCredentialsParams{ + Context: ctx, + } +} + +// NewListGiteaCredentialsParamsWithHTTPClient creates a new ListGiteaCredentialsParams object +// with the ability to set a custom HTTPClient for a request. +func NewListGiteaCredentialsParamsWithHTTPClient(client *http.Client) *ListGiteaCredentialsParams { + return &ListGiteaCredentialsParams{ + HTTPClient: client, + } +} + +/* +ListGiteaCredentialsParams contains all the parameters to send to the API endpoint + + for the list gitea credentials operation. + + Typically these are written to a http.Request. +*/ +type ListGiteaCredentialsParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the list gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListGiteaCredentialsParams) WithDefaults() *ListGiteaCredentialsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the list gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListGiteaCredentialsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the list gitea credentials params +func (o *ListGiteaCredentialsParams) WithTimeout(timeout time.Duration) *ListGiteaCredentialsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the list gitea credentials params +func (o *ListGiteaCredentialsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the list gitea credentials params +func (o *ListGiteaCredentialsParams) WithContext(ctx context.Context) *ListGiteaCredentialsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the list gitea credentials params +func (o *ListGiteaCredentialsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the list gitea credentials params +func (o *ListGiteaCredentialsParams) WithHTTPClient(client *http.Client) *ListGiteaCredentialsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the list gitea credentials params +func (o *ListGiteaCredentialsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *ListGiteaCredentialsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/credentials/list_gitea_credentials_responses.go b/client/credentials/list_gitea_credentials_responses.go new file mode 100644 index 00000000..f27864be --- /dev/null +++ b/client/credentials/list_gitea_credentials_responses.go @@ -0,0 +1,179 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// ListGiteaCredentialsReader is a Reader for the ListGiteaCredentials structure. +type ListGiteaCredentialsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *ListGiteaCredentialsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewListGiteaCredentialsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewListGiteaCredentialsBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("[GET /gitea/credentials] ListGiteaCredentials", response, response.Code()) + } +} + +// NewListGiteaCredentialsOK creates a ListGiteaCredentialsOK with default headers values +func NewListGiteaCredentialsOK() *ListGiteaCredentialsOK { + return &ListGiteaCredentialsOK{} +} + +/* +ListGiteaCredentialsOK describes a response with status code 200, with default header values. + +Credentials +*/ +type ListGiteaCredentialsOK struct { + Payload garm_params.Credentials +} + +// IsSuccess returns true when this list gitea credentials o k response has a 2xx status code +func (o *ListGiteaCredentialsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this list gitea credentials o k response has a 3xx status code +func (o *ListGiteaCredentialsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this list gitea credentials o k response has a 4xx status code +func (o *ListGiteaCredentialsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this list gitea credentials o k response has a 5xx status code +func (o *ListGiteaCredentialsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this list gitea credentials o k response a status code equal to that given +func (o *ListGiteaCredentialsOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the list gitea credentials o k response +func (o *ListGiteaCredentialsOK) Code() int { + return 200 +} + +func (o *ListGiteaCredentialsOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/credentials][%d] listGiteaCredentialsOK %s", 200, payload) +} + +func (o *ListGiteaCredentialsOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/credentials][%d] listGiteaCredentialsOK %s", 200, payload) +} + +func (o *ListGiteaCredentialsOK) GetPayload() garm_params.Credentials { + return o.Payload +} + +func (o *ListGiteaCredentialsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewListGiteaCredentialsBadRequest creates a ListGiteaCredentialsBadRequest with default headers values +func NewListGiteaCredentialsBadRequest() *ListGiteaCredentialsBadRequest { + return &ListGiteaCredentialsBadRequest{} +} + +/* +ListGiteaCredentialsBadRequest describes a response with status code 400, with default header values. + +APIErrorResponse +*/ +type ListGiteaCredentialsBadRequest struct { + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this list gitea credentials bad request response has a 2xx status code +func (o *ListGiteaCredentialsBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this list gitea credentials bad request response has a 3xx status code +func (o *ListGiteaCredentialsBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this list gitea credentials bad request response has a 4xx status code +func (o *ListGiteaCredentialsBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this list gitea credentials bad request response has a 5xx status code +func (o *ListGiteaCredentialsBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this list gitea credentials bad request response a status code equal to that given +func (o *ListGiteaCredentialsBadRequest) IsCode(code int) bool { + return code == 400 +} + +// Code gets the status code for the list gitea credentials bad request response +func (o *ListGiteaCredentialsBadRequest) Code() int { + return 400 +} + +func (o *ListGiteaCredentialsBadRequest) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/credentials][%d] listGiteaCredentialsBadRequest %s", 400, payload) +} + +func (o *ListGiteaCredentialsBadRequest) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/credentials][%d] listGiteaCredentialsBadRequest %s", 400, payload) +} + +func (o *ListGiteaCredentialsBadRequest) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *ListGiteaCredentialsBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/credentials/update_credentials_responses.go b/client/credentials/update_credentials_responses.go index b0254604..6a9f37f8 100644 --- a/client/credentials/update_credentials_responses.go +++ b/client/credentials/update_credentials_responses.go @@ -50,10 +50,10 @@ func NewUpdateCredentialsOK() *UpdateCredentialsOK { /* UpdateCredentialsOK describes a response with status code 200, with default header values. -GithubCredentials +ForgeCredentials */ type UpdateCredentialsOK struct { - Payload garm_params.GithubCredentials + Payload garm_params.ForgeCredentials } // IsSuccess returns true when this update credentials o k response has a 2xx status code @@ -96,7 +96,7 @@ func (o *UpdateCredentialsOK) String() string { return fmt.Sprintf("[PUT /github/credentials/{id}][%d] updateCredentialsOK %s", 200, payload) } -func (o *UpdateCredentialsOK) GetPayload() garm_params.GithubCredentials { +func (o *UpdateCredentialsOK) GetPayload() garm_params.ForgeCredentials { return o.Payload } diff --git a/client/credentials/update_gitea_credentials_parameters.go b/client/credentials/update_gitea_credentials_parameters.go new file mode 100644 index 00000000..1907a0f2 --- /dev/null +++ b/client/credentials/update_gitea_credentials_parameters.go @@ -0,0 +1,174 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + + garm_params "github.com/cloudbase/garm/params" +) + +// NewUpdateGiteaCredentialsParams creates a new UpdateGiteaCredentialsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewUpdateGiteaCredentialsParams() *UpdateGiteaCredentialsParams { + return &UpdateGiteaCredentialsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewUpdateGiteaCredentialsParamsWithTimeout creates a new UpdateGiteaCredentialsParams object +// with the ability to set a timeout on a request. +func NewUpdateGiteaCredentialsParamsWithTimeout(timeout time.Duration) *UpdateGiteaCredentialsParams { + return &UpdateGiteaCredentialsParams{ + timeout: timeout, + } +} + +// NewUpdateGiteaCredentialsParamsWithContext creates a new UpdateGiteaCredentialsParams object +// with the ability to set a context for a request. +func NewUpdateGiteaCredentialsParamsWithContext(ctx context.Context) *UpdateGiteaCredentialsParams { + return &UpdateGiteaCredentialsParams{ + Context: ctx, + } +} + +// NewUpdateGiteaCredentialsParamsWithHTTPClient creates a new UpdateGiteaCredentialsParams object +// with the ability to set a custom HTTPClient for a request. +func NewUpdateGiteaCredentialsParamsWithHTTPClient(client *http.Client) *UpdateGiteaCredentialsParams { + return &UpdateGiteaCredentialsParams{ + HTTPClient: client, + } +} + +/* +UpdateGiteaCredentialsParams contains all the parameters to send to the API endpoint + + for the update gitea credentials operation. + + Typically these are written to a http.Request. +*/ +type UpdateGiteaCredentialsParams struct { + + /* Body. + + Parameters used when updating a Gitea credential. + */ + Body garm_params.UpdateGiteaCredentialsParams + + /* ID. + + ID of the Gitea credential. + */ + ID int64 + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the update gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *UpdateGiteaCredentialsParams) WithDefaults() *UpdateGiteaCredentialsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the update gitea credentials params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *UpdateGiteaCredentialsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) WithTimeout(timeout time.Duration) *UpdateGiteaCredentialsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) WithContext(ctx context.Context) *UpdateGiteaCredentialsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) WithHTTPClient(client *http.Client) *UpdateGiteaCredentialsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) WithBody(body garm_params.UpdateGiteaCredentialsParams) *UpdateGiteaCredentialsParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) SetBody(body garm_params.UpdateGiteaCredentialsParams) { + o.Body = body +} + +// WithID adds the id to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) WithID(id int64) *UpdateGiteaCredentialsParams { + o.SetID(id) + return o +} + +// SetID adds the id to the update gitea credentials params +func (o *UpdateGiteaCredentialsParams) SetID(id int64) { + o.ID = id +} + +// WriteToRequest writes these params to a swagger request +func (o *UpdateGiteaCredentialsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + // path param id + if err := r.SetPathParam("id", swag.FormatInt64(o.ID)); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/credentials/update_gitea_credentials_responses.go b/client/credentials/update_gitea_credentials_responses.go new file mode 100644 index 00000000..edbb54d8 --- /dev/null +++ b/client/credentials/update_gitea_credentials_responses.go @@ -0,0 +1,179 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package credentials + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// UpdateGiteaCredentialsReader is a Reader for the UpdateGiteaCredentials structure. +type UpdateGiteaCredentialsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *UpdateGiteaCredentialsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewUpdateGiteaCredentialsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewUpdateGiteaCredentialsBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("[PUT /gitea/credentials/{id}] UpdateGiteaCredentials", response, response.Code()) + } +} + +// NewUpdateGiteaCredentialsOK creates a UpdateGiteaCredentialsOK with default headers values +func NewUpdateGiteaCredentialsOK() *UpdateGiteaCredentialsOK { + return &UpdateGiteaCredentialsOK{} +} + +/* +UpdateGiteaCredentialsOK describes a response with status code 200, with default header values. + +ForgeCredentials +*/ +type UpdateGiteaCredentialsOK struct { + Payload garm_params.ForgeCredentials +} + +// IsSuccess returns true when this update gitea credentials o k response has a 2xx status code +func (o *UpdateGiteaCredentialsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this update gitea credentials o k response has a 3xx status code +func (o *UpdateGiteaCredentialsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this update gitea credentials o k response has a 4xx status code +func (o *UpdateGiteaCredentialsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this update gitea credentials o k response has a 5xx status code +func (o *UpdateGiteaCredentialsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this update gitea credentials o k response a status code equal to that given +func (o *UpdateGiteaCredentialsOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the update gitea credentials o k response +func (o *UpdateGiteaCredentialsOK) Code() int { + return 200 +} + +func (o *UpdateGiteaCredentialsOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /gitea/credentials/{id}][%d] updateGiteaCredentialsOK %s", 200, payload) +} + +func (o *UpdateGiteaCredentialsOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /gitea/credentials/{id}][%d] updateGiteaCredentialsOK %s", 200, payload) +} + +func (o *UpdateGiteaCredentialsOK) GetPayload() garm_params.ForgeCredentials { + return o.Payload +} + +func (o *UpdateGiteaCredentialsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewUpdateGiteaCredentialsBadRequest creates a UpdateGiteaCredentialsBadRequest with default headers values +func NewUpdateGiteaCredentialsBadRequest() *UpdateGiteaCredentialsBadRequest { + return &UpdateGiteaCredentialsBadRequest{} +} + +/* +UpdateGiteaCredentialsBadRequest describes a response with status code 400, with default header values. + +APIErrorResponse +*/ +type UpdateGiteaCredentialsBadRequest struct { + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this update gitea credentials bad request response has a 2xx status code +func (o *UpdateGiteaCredentialsBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this update gitea credentials bad request response has a 3xx status code +func (o *UpdateGiteaCredentialsBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this update gitea credentials bad request response has a 4xx status code +func (o *UpdateGiteaCredentialsBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this update gitea credentials bad request response has a 5xx status code +func (o *UpdateGiteaCredentialsBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this update gitea credentials bad request response a status code equal to that given +func (o *UpdateGiteaCredentialsBadRequest) IsCode(code int) bool { + return code == 400 +} + +// Code gets the status code for the update gitea credentials bad request response +func (o *UpdateGiteaCredentialsBadRequest) Code() int { + return 400 +} + +func (o *UpdateGiteaCredentialsBadRequest) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /gitea/credentials/{id}][%d] updateGiteaCredentialsBadRequest %s", 400, payload) +} + +func (o *UpdateGiteaCredentialsBadRequest) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /gitea/credentials/{id}][%d] updateGiteaCredentialsBadRequest %s", 400, payload) +} + +func (o *UpdateGiteaCredentialsBadRequest) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *UpdateGiteaCredentialsBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/endpoints/create_gitea_endpoint_parameters.go b/client/endpoints/create_gitea_endpoint_parameters.go new file mode 100644 index 00000000..11dfa73f --- /dev/null +++ b/client/endpoints/create_gitea_endpoint_parameters.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + garm_params "github.com/cloudbase/garm/params" +) + +// NewCreateGiteaEndpointParams creates a new CreateGiteaEndpointParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewCreateGiteaEndpointParams() *CreateGiteaEndpointParams { + return &CreateGiteaEndpointParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewCreateGiteaEndpointParamsWithTimeout creates a new CreateGiteaEndpointParams object +// with the ability to set a timeout on a request. +func NewCreateGiteaEndpointParamsWithTimeout(timeout time.Duration) *CreateGiteaEndpointParams { + return &CreateGiteaEndpointParams{ + timeout: timeout, + } +} + +// NewCreateGiteaEndpointParamsWithContext creates a new CreateGiteaEndpointParams object +// with the ability to set a context for a request. +func NewCreateGiteaEndpointParamsWithContext(ctx context.Context) *CreateGiteaEndpointParams { + return &CreateGiteaEndpointParams{ + Context: ctx, + } +} + +// NewCreateGiteaEndpointParamsWithHTTPClient creates a new CreateGiteaEndpointParams object +// with the ability to set a custom HTTPClient for a request. +func NewCreateGiteaEndpointParamsWithHTTPClient(client *http.Client) *CreateGiteaEndpointParams { + return &CreateGiteaEndpointParams{ + HTTPClient: client, + } +} + +/* +CreateGiteaEndpointParams contains all the parameters to send to the API endpoint + + for the create gitea endpoint operation. + + Typically these are written to a http.Request. +*/ +type CreateGiteaEndpointParams struct { + + /* Body. + + Parameters used when creating a Gitea endpoint. + */ + Body garm_params.CreateGiteaEndpointParams + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the create gitea endpoint params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *CreateGiteaEndpointParams) WithDefaults() *CreateGiteaEndpointParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the create gitea endpoint params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *CreateGiteaEndpointParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the create gitea endpoint params +func (o *CreateGiteaEndpointParams) WithTimeout(timeout time.Duration) *CreateGiteaEndpointParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the create gitea endpoint params +func (o *CreateGiteaEndpointParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the create gitea endpoint params +func (o *CreateGiteaEndpointParams) WithContext(ctx context.Context) *CreateGiteaEndpointParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the create gitea endpoint params +func (o *CreateGiteaEndpointParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the create gitea endpoint params +func (o *CreateGiteaEndpointParams) WithHTTPClient(client *http.Client) *CreateGiteaEndpointParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the create gitea endpoint params +func (o *CreateGiteaEndpointParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the create gitea endpoint params +func (o *CreateGiteaEndpointParams) WithBody(body garm_params.CreateGiteaEndpointParams) *CreateGiteaEndpointParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the create gitea endpoint params +func (o *CreateGiteaEndpointParams) SetBody(body garm_params.CreateGiteaEndpointParams) { + o.Body = body +} + +// WriteToRequest writes these params to a swagger request +func (o *CreateGiteaEndpointParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/endpoints/create_gitea_endpoint_responses.go b/client/endpoints/create_gitea_endpoint_responses.go new file mode 100644 index 00000000..6e99a973 --- /dev/null +++ b/client/endpoints/create_gitea_endpoint_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// CreateGiteaEndpointReader is a Reader for the CreateGiteaEndpoint structure. +type CreateGiteaEndpointReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *CreateGiteaEndpointReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewCreateGiteaEndpointOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewCreateGiteaEndpointDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewCreateGiteaEndpointOK creates a CreateGiteaEndpointOK with default headers values +func NewCreateGiteaEndpointOK() *CreateGiteaEndpointOK { + return &CreateGiteaEndpointOK{} +} + +/* +CreateGiteaEndpointOK describes a response with status code 200, with default header values. + +ForgeEndpoint +*/ +type CreateGiteaEndpointOK struct { + Payload garm_params.ForgeEndpoint +} + +// IsSuccess returns true when this create gitea endpoint o k response has a 2xx status code +func (o *CreateGiteaEndpointOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this create gitea endpoint o k response has a 3xx status code +func (o *CreateGiteaEndpointOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this create gitea endpoint o k response has a 4xx status code +func (o *CreateGiteaEndpointOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this create gitea endpoint o k response has a 5xx status code +func (o *CreateGiteaEndpointOK) IsServerError() bool { + return false +} + +// IsCode returns true when this create gitea endpoint o k response a status code equal to that given +func (o *CreateGiteaEndpointOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the create gitea endpoint o k response +func (o *CreateGiteaEndpointOK) Code() int { + return 200 +} + +func (o *CreateGiteaEndpointOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[POST /gitea/endpoints][%d] createGiteaEndpointOK %s", 200, payload) +} + +func (o *CreateGiteaEndpointOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[POST /gitea/endpoints][%d] createGiteaEndpointOK %s", 200, payload) +} + +func (o *CreateGiteaEndpointOK) GetPayload() garm_params.ForgeEndpoint { + return o.Payload +} + +func (o *CreateGiteaEndpointOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewCreateGiteaEndpointDefault creates a CreateGiteaEndpointDefault with default headers values +func NewCreateGiteaEndpointDefault(code int) *CreateGiteaEndpointDefault { + return &CreateGiteaEndpointDefault{ + _statusCode: code, + } +} + +/* +CreateGiteaEndpointDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type CreateGiteaEndpointDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this create gitea endpoint default response has a 2xx status code +func (o *CreateGiteaEndpointDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this create gitea endpoint default response has a 3xx status code +func (o *CreateGiteaEndpointDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this create gitea endpoint default response has a 4xx status code +func (o *CreateGiteaEndpointDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this create gitea endpoint default response has a 5xx status code +func (o *CreateGiteaEndpointDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this create gitea endpoint default response a status code equal to that given +func (o *CreateGiteaEndpointDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the create gitea endpoint default response +func (o *CreateGiteaEndpointDefault) Code() int { + return o._statusCode +} + +func (o *CreateGiteaEndpointDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[POST /gitea/endpoints][%d] CreateGiteaEndpoint default %s", o._statusCode, payload) +} + +func (o *CreateGiteaEndpointDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[POST /gitea/endpoints][%d] CreateGiteaEndpoint default %s", o._statusCode, payload) +} + +func (o *CreateGiteaEndpointDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *CreateGiteaEndpointDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/endpoints/create_github_endpoint_responses.go b/client/endpoints/create_github_endpoint_responses.go index 57016978..60961f3a 100644 --- a/client/endpoints/create_github_endpoint_responses.go +++ b/client/endpoints/create_github_endpoint_responses.go @@ -51,7 +51,7 @@ func NewCreateGithubEndpointOK() *CreateGithubEndpointOK { /* CreateGithubEndpointOK describes a response with status code 200, with default header values. -GithubEndpoint +ForgeEndpoint */ type CreateGithubEndpointOK struct { Payload garm_params.ForgeEndpoint diff --git a/client/endpoints/delete_gitea_endpoint_parameters.go b/client/endpoints/delete_gitea_endpoint_parameters.go new file mode 100644 index 00000000..f7ea5a5d --- /dev/null +++ b/client/endpoints/delete_gitea_endpoint_parameters.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewDeleteGiteaEndpointParams creates a new DeleteGiteaEndpointParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewDeleteGiteaEndpointParams() *DeleteGiteaEndpointParams { + return &DeleteGiteaEndpointParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewDeleteGiteaEndpointParamsWithTimeout creates a new DeleteGiteaEndpointParams object +// with the ability to set a timeout on a request. +func NewDeleteGiteaEndpointParamsWithTimeout(timeout time.Duration) *DeleteGiteaEndpointParams { + return &DeleteGiteaEndpointParams{ + timeout: timeout, + } +} + +// NewDeleteGiteaEndpointParamsWithContext creates a new DeleteGiteaEndpointParams object +// with the ability to set a context for a request. +func NewDeleteGiteaEndpointParamsWithContext(ctx context.Context) *DeleteGiteaEndpointParams { + return &DeleteGiteaEndpointParams{ + Context: ctx, + } +} + +// NewDeleteGiteaEndpointParamsWithHTTPClient creates a new DeleteGiteaEndpointParams object +// with the ability to set a custom HTTPClient for a request. +func NewDeleteGiteaEndpointParamsWithHTTPClient(client *http.Client) *DeleteGiteaEndpointParams { + return &DeleteGiteaEndpointParams{ + HTTPClient: client, + } +} + +/* +DeleteGiteaEndpointParams contains all the parameters to send to the API endpoint + + for the delete gitea endpoint operation. + + Typically these are written to a http.Request. +*/ +type DeleteGiteaEndpointParams struct { + + /* Name. + + The name of the Gitea endpoint. + */ + Name string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the delete gitea endpoint params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteGiteaEndpointParams) WithDefaults() *DeleteGiteaEndpointParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the delete gitea endpoint params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteGiteaEndpointParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the delete gitea endpoint params +func (o *DeleteGiteaEndpointParams) WithTimeout(timeout time.Duration) *DeleteGiteaEndpointParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the delete gitea endpoint params +func (o *DeleteGiteaEndpointParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the delete gitea endpoint params +func (o *DeleteGiteaEndpointParams) WithContext(ctx context.Context) *DeleteGiteaEndpointParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the delete gitea endpoint params +func (o *DeleteGiteaEndpointParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the delete gitea endpoint params +func (o *DeleteGiteaEndpointParams) WithHTTPClient(client *http.Client) *DeleteGiteaEndpointParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the delete gitea endpoint params +func (o *DeleteGiteaEndpointParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithName adds the name to the delete gitea endpoint params +func (o *DeleteGiteaEndpointParams) WithName(name string) *DeleteGiteaEndpointParams { + o.SetName(name) + return o +} + +// SetName adds the name to the delete gitea endpoint params +func (o *DeleteGiteaEndpointParams) SetName(name string) { + o.Name = name +} + +// WriteToRequest writes these params to a swagger request +func (o *DeleteGiteaEndpointParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param name + if err := r.SetPathParam("name", o.Name); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/endpoints/delete_gitea_endpoint_responses.go b/client/endpoints/delete_gitea_endpoint_responses.go new file mode 100644 index 00000000..787d6585 --- /dev/null +++ b/client/endpoints/delete_gitea_endpoint_responses.go @@ -0,0 +1,106 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" +) + +// DeleteGiteaEndpointReader is a Reader for the DeleteGiteaEndpoint structure. +type DeleteGiteaEndpointReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *DeleteGiteaEndpointReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + result := NewDeleteGiteaEndpointDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result +} + +// NewDeleteGiteaEndpointDefault creates a DeleteGiteaEndpointDefault with default headers values +func NewDeleteGiteaEndpointDefault(code int) *DeleteGiteaEndpointDefault { + return &DeleteGiteaEndpointDefault{ + _statusCode: code, + } +} + +/* +DeleteGiteaEndpointDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type DeleteGiteaEndpointDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this delete gitea endpoint default response has a 2xx status code +func (o *DeleteGiteaEndpointDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this delete gitea endpoint default response has a 3xx status code +func (o *DeleteGiteaEndpointDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this delete gitea endpoint default response has a 4xx status code +func (o *DeleteGiteaEndpointDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this delete gitea endpoint default response has a 5xx status code +func (o *DeleteGiteaEndpointDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this delete gitea endpoint default response a status code equal to that given +func (o *DeleteGiteaEndpointDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the delete gitea endpoint default response +func (o *DeleteGiteaEndpointDefault) Code() int { + return o._statusCode +} + +func (o *DeleteGiteaEndpointDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[DELETE /gitea/endpoints/{name}][%d] DeleteGiteaEndpoint default %s", o._statusCode, payload) +} + +func (o *DeleteGiteaEndpointDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[DELETE /gitea/endpoints/{name}][%d] DeleteGiteaEndpoint default %s", o._statusCode, payload) +} + +func (o *DeleteGiteaEndpointDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *DeleteGiteaEndpointDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/endpoints/endpoints_client.go b/client/endpoints/endpoints_client.go index 9b951b2c..74019577 100644 --- a/client/endpoints/endpoints_client.go +++ b/client/endpoints/endpoints_client.go @@ -54,19 +54,67 @@ type ClientOption func(*runtime.ClientOperation) // ClientService is the interface for Client methods type ClientService interface { + CreateGiteaEndpoint(params *CreateGiteaEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*CreateGiteaEndpointOK, error) + CreateGithubEndpoint(params *CreateGithubEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*CreateGithubEndpointOK, error) + DeleteGiteaEndpoint(params *DeleteGiteaEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) error + DeleteGithubEndpoint(params *DeleteGithubEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) error + GetGiteaEndpoint(params *GetGiteaEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetGiteaEndpointOK, error) + GetGithubEndpoint(params *GetGithubEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetGithubEndpointOK, error) + ListGiteaEndpoints(params *ListGiteaEndpointsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListGiteaEndpointsOK, error) + ListGithubEndpoints(params *ListGithubEndpointsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListGithubEndpointsOK, error) + UpdateGiteaEndpoint(params *UpdateGiteaEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*UpdateGiteaEndpointOK, error) + UpdateGithubEndpoint(params *UpdateGithubEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*UpdateGithubEndpointOK, error) SetTransport(transport runtime.ClientTransport) } +/* +CreateGiteaEndpoint creates a gitea endpoint +*/ +func (a *Client) CreateGiteaEndpoint(params *CreateGiteaEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*CreateGiteaEndpointOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewCreateGiteaEndpointParams() + } + op := &runtime.ClientOperation{ + ID: "CreateGiteaEndpoint", + Method: "POST", + PathPattern: "/gitea/endpoints", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &CreateGiteaEndpointReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*CreateGiteaEndpointOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*CreateGiteaEndpointDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + /* CreateGithubEndpoint creates a git hub endpoint */ @@ -105,6 +153,38 @@ func (a *Client) CreateGithubEndpoint(params *CreateGithubEndpointParams, authIn return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } +/* +DeleteGiteaEndpoint deletes a gitea endpoint +*/ +func (a *Client) DeleteGiteaEndpoint(params *DeleteGiteaEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) error { + // TODO: Validate the params before sending + if params == nil { + params = NewDeleteGiteaEndpointParams() + } + op := &runtime.ClientOperation{ + ID: "DeleteGiteaEndpoint", + Method: "DELETE", + PathPattern: "/gitea/endpoints/{name}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &DeleteGiteaEndpointReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + _, err := a.transport.Submit(op) + if err != nil { + return err + } + return nil +} + /* DeleteGithubEndpoint deletes a git hub endpoint */ @@ -137,6 +217,44 @@ func (a *Client) DeleteGithubEndpoint(params *DeleteGithubEndpointParams, authIn return nil } +/* +GetGiteaEndpoint gets a gitea endpoint +*/ +func (a *Client) GetGiteaEndpoint(params *GetGiteaEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetGiteaEndpointOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetGiteaEndpointParams() + } + op := &runtime.ClientOperation{ + ID: "GetGiteaEndpoint", + Method: "GET", + PathPattern: "/gitea/endpoints/{name}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetGiteaEndpointReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetGiteaEndpointOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*GetGiteaEndpointDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + /* GetGithubEndpoint gets a git hub endpoint */ @@ -175,6 +293,44 @@ func (a *Client) GetGithubEndpoint(params *GetGithubEndpointParams, authInfo run return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } +/* +ListGiteaEndpoints lists all gitea endpoints +*/ +func (a *Client) ListGiteaEndpoints(params *ListGiteaEndpointsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListGiteaEndpointsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewListGiteaEndpointsParams() + } + op := &runtime.ClientOperation{ + ID: "ListGiteaEndpoints", + Method: "GET", + PathPattern: "/gitea/endpoints", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &ListGiteaEndpointsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*ListGiteaEndpointsOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*ListGiteaEndpointsDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + /* ListGithubEndpoints lists all git hub endpoints */ @@ -213,6 +369,44 @@ func (a *Client) ListGithubEndpoints(params *ListGithubEndpointsParams, authInfo return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } +/* +UpdateGiteaEndpoint updates a gitea endpoint +*/ +func (a *Client) UpdateGiteaEndpoint(params *UpdateGiteaEndpointParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*UpdateGiteaEndpointOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewUpdateGiteaEndpointParams() + } + op := &runtime.ClientOperation{ + ID: "UpdateGiteaEndpoint", + Method: "PUT", + PathPattern: "/gitea/endpoints/{name}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &UpdateGiteaEndpointReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*UpdateGiteaEndpointOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*UpdateGiteaEndpointDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + /* UpdateGithubEndpoint updates a git hub endpoint */ diff --git a/client/endpoints/get_gitea_endpoint_parameters.go b/client/endpoints/get_gitea_endpoint_parameters.go new file mode 100644 index 00000000..0d7f883b --- /dev/null +++ b/client/endpoints/get_gitea_endpoint_parameters.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetGiteaEndpointParams creates a new GetGiteaEndpointParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetGiteaEndpointParams() *GetGiteaEndpointParams { + return &GetGiteaEndpointParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetGiteaEndpointParamsWithTimeout creates a new GetGiteaEndpointParams object +// with the ability to set a timeout on a request. +func NewGetGiteaEndpointParamsWithTimeout(timeout time.Duration) *GetGiteaEndpointParams { + return &GetGiteaEndpointParams{ + timeout: timeout, + } +} + +// NewGetGiteaEndpointParamsWithContext creates a new GetGiteaEndpointParams object +// with the ability to set a context for a request. +func NewGetGiteaEndpointParamsWithContext(ctx context.Context) *GetGiteaEndpointParams { + return &GetGiteaEndpointParams{ + Context: ctx, + } +} + +// NewGetGiteaEndpointParamsWithHTTPClient creates a new GetGiteaEndpointParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetGiteaEndpointParamsWithHTTPClient(client *http.Client) *GetGiteaEndpointParams { + return &GetGiteaEndpointParams{ + HTTPClient: client, + } +} + +/* +GetGiteaEndpointParams contains all the parameters to send to the API endpoint + + for the get gitea endpoint operation. + + Typically these are written to a http.Request. +*/ +type GetGiteaEndpointParams struct { + + /* Name. + + The name of the Gitea endpoint. + */ + Name string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get gitea endpoint params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetGiteaEndpointParams) WithDefaults() *GetGiteaEndpointParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get gitea endpoint params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetGiteaEndpointParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get gitea endpoint params +func (o *GetGiteaEndpointParams) WithTimeout(timeout time.Duration) *GetGiteaEndpointParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get gitea endpoint params +func (o *GetGiteaEndpointParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get gitea endpoint params +func (o *GetGiteaEndpointParams) WithContext(ctx context.Context) *GetGiteaEndpointParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get gitea endpoint params +func (o *GetGiteaEndpointParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get gitea endpoint params +func (o *GetGiteaEndpointParams) WithHTTPClient(client *http.Client) *GetGiteaEndpointParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get gitea endpoint params +func (o *GetGiteaEndpointParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithName adds the name to the get gitea endpoint params +func (o *GetGiteaEndpointParams) WithName(name string) *GetGiteaEndpointParams { + o.SetName(name) + return o +} + +// SetName adds the name to the get gitea endpoint params +func (o *GetGiteaEndpointParams) SetName(name string) { + o.Name = name +} + +// WriteToRequest writes these params to a swagger request +func (o *GetGiteaEndpointParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param name + if err := r.SetPathParam("name", o.Name); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/endpoints/get_gitea_endpoint_responses.go b/client/endpoints/get_gitea_endpoint_responses.go new file mode 100644 index 00000000..e4bacd03 --- /dev/null +++ b/client/endpoints/get_gitea_endpoint_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// GetGiteaEndpointReader is a Reader for the GetGiteaEndpoint structure. +type GetGiteaEndpointReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetGiteaEndpointReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetGiteaEndpointOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewGetGiteaEndpointDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewGetGiteaEndpointOK creates a GetGiteaEndpointOK with default headers values +func NewGetGiteaEndpointOK() *GetGiteaEndpointOK { + return &GetGiteaEndpointOK{} +} + +/* +GetGiteaEndpointOK describes a response with status code 200, with default header values. + +ForgeEndpoint +*/ +type GetGiteaEndpointOK struct { + Payload garm_params.ForgeEndpoint +} + +// IsSuccess returns true when this get gitea endpoint o k response has a 2xx status code +func (o *GetGiteaEndpointOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get gitea endpoint o k response has a 3xx status code +func (o *GetGiteaEndpointOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get gitea endpoint o k response has a 4xx status code +func (o *GetGiteaEndpointOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get gitea endpoint o k response has a 5xx status code +func (o *GetGiteaEndpointOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get gitea endpoint o k response a status code equal to that given +func (o *GetGiteaEndpointOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the get gitea endpoint o k response +func (o *GetGiteaEndpointOK) Code() int { + return 200 +} + +func (o *GetGiteaEndpointOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/endpoints/{name}][%d] getGiteaEndpointOK %s", 200, payload) +} + +func (o *GetGiteaEndpointOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/endpoints/{name}][%d] getGiteaEndpointOK %s", 200, payload) +} + +func (o *GetGiteaEndpointOK) GetPayload() garm_params.ForgeEndpoint { + return o.Payload +} + +func (o *GetGiteaEndpointOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetGiteaEndpointDefault creates a GetGiteaEndpointDefault with default headers values +func NewGetGiteaEndpointDefault(code int) *GetGiteaEndpointDefault { + return &GetGiteaEndpointDefault{ + _statusCode: code, + } +} + +/* +GetGiteaEndpointDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type GetGiteaEndpointDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this get gitea endpoint default response has a 2xx status code +func (o *GetGiteaEndpointDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this get gitea endpoint default response has a 3xx status code +func (o *GetGiteaEndpointDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this get gitea endpoint default response has a 4xx status code +func (o *GetGiteaEndpointDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this get gitea endpoint default response has a 5xx status code +func (o *GetGiteaEndpointDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this get gitea endpoint default response a status code equal to that given +func (o *GetGiteaEndpointDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the get gitea endpoint default response +func (o *GetGiteaEndpointDefault) Code() int { + return o._statusCode +} + +func (o *GetGiteaEndpointDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/endpoints/{name}][%d] GetGiteaEndpoint default %s", o._statusCode, payload) +} + +func (o *GetGiteaEndpointDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/endpoints/{name}][%d] GetGiteaEndpoint default %s", o._statusCode, payload) +} + +func (o *GetGiteaEndpointDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *GetGiteaEndpointDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/endpoints/get_github_endpoint_responses.go b/client/endpoints/get_github_endpoint_responses.go index 4b4881cd..e2b97a60 100644 --- a/client/endpoints/get_github_endpoint_responses.go +++ b/client/endpoints/get_github_endpoint_responses.go @@ -51,7 +51,7 @@ func NewGetGithubEndpointOK() *GetGithubEndpointOK { /* GetGithubEndpointOK describes a response with status code 200, with default header values. -GithubEndpoint +ForgeEndpoint */ type GetGithubEndpointOK struct { Payload garm_params.ForgeEndpoint diff --git a/client/endpoints/list_gitea_endpoints_parameters.go b/client/endpoints/list_gitea_endpoints_parameters.go new file mode 100644 index 00000000..93ec6ae6 --- /dev/null +++ b/client/endpoints/list_gitea_endpoints_parameters.go @@ -0,0 +1,128 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewListGiteaEndpointsParams creates a new ListGiteaEndpointsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewListGiteaEndpointsParams() *ListGiteaEndpointsParams { + return &ListGiteaEndpointsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewListGiteaEndpointsParamsWithTimeout creates a new ListGiteaEndpointsParams object +// with the ability to set a timeout on a request. +func NewListGiteaEndpointsParamsWithTimeout(timeout time.Duration) *ListGiteaEndpointsParams { + return &ListGiteaEndpointsParams{ + timeout: timeout, + } +} + +// NewListGiteaEndpointsParamsWithContext creates a new ListGiteaEndpointsParams object +// with the ability to set a context for a request. +func NewListGiteaEndpointsParamsWithContext(ctx context.Context) *ListGiteaEndpointsParams { + return &ListGiteaEndpointsParams{ + Context: ctx, + } +} + +// NewListGiteaEndpointsParamsWithHTTPClient creates a new ListGiteaEndpointsParams object +// with the ability to set a custom HTTPClient for a request. +func NewListGiteaEndpointsParamsWithHTTPClient(client *http.Client) *ListGiteaEndpointsParams { + return &ListGiteaEndpointsParams{ + HTTPClient: client, + } +} + +/* +ListGiteaEndpointsParams contains all the parameters to send to the API endpoint + + for the list gitea endpoints operation. + + Typically these are written to a http.Request. +*/ +type ListGiteaEndpointsParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the list gitea endpoints params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListGiteaEndpointsParams) WithDefaults() *ListGiteaEndpointsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the list gitea endpoints params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListGiteaEndpointsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the list gitea endpoints params +func (o *ListGiteaEndpointsParams) WithTimeout(timeout time.Duration) *ListGiteaEndpointsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the list gitea endpoints params +func (o *ListGiteaEndpointsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the list gitea endpoints params +func (o *ListGiteaEndpointsParams) WithContext(ctx context.Context) *ListGiteaEndpointsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the list gitea endpoints params +func (o *ListGiteaEndpointsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the list gitea endpoints params +func (o *ListGiteaEndpointsParams) WithHTTPClient(client *http.Client) *ListGiteaEndpointsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the list gitea endpoints params +func (o *ListGiteaEndpointsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *ListGiteaEndpointsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/endpoints/list_gitea_endpoints_responses.go b/client/endpoints/list_gitea_endpoints_responses.go new file mode 100644 index 00000000..0fdd90ec --- /dev/null +++ b/client/endpoints/list_gitea_endpoints_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// ListGiteaEndpointsReader is a Reader for the ListGiteaEndpoints structure. +type ListGiteaEndpointsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *ListGiteaEndpointsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewListGiteaEndpointsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewListGiteaEndpointsDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewListGiteaEndpointsOK creates a ListGiteaEndpointsOK with default headers values +func NewListGiteaEndpointsOK() *ListGiteaEndpointsOK { + return &ListGiteaEndpointsOK{} +} + +/* +ListGiteaEndpointsOK describes a response with status code 200, with default header values. + +ForgeEndpoints +*/ +type ListGiteaEndpointsOK struct { + Payload garm_params.ForgeEndpoints +} + +// IsSuccess returns true when this list gitea endpoints o k response has a 2xx status code +func (o *ListGiteaEndpointsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this list gitea endpoints o k response has a 3xx status code +func (o *ListGiteaEndpointsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this list gitea endpoints o k response has a 4xx status code +func (o *ListGiteaEndpointsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this list gitea endpoints o k response has a 5xx status code +func (o *ListGiteaEndpointsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this list gitea endpoints o k response a status code equal to that given +func (o *ListGiteaEndpointsOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the list gitea endpoints o k response +func (o *ListGiteaEndpointsOK) Code() int { + return 200 +} + +func (o *ListGiteaEndpointsOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/endpoints][%d] listGiteaEndpointsOK %s", 200, payload) +} + +func (o *ListGiteaEndpointsOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/endpoints][%d] listGiteaEndpointsOK %s", 200, payload) +} + +func (o *ListGiteaEndpointsOK) GetPayload() garm_params.ForgeEndpoints { + return o.Payload +} + +func (o *ListGiteaEndpointsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewListGiteaEndpointsDefault creates a ListGiteaEndpointsDefault with default headers values +func NewListGiteaEndpointsDefault(code int) *ListGiteaEndpointsDefault { + return &ListGiteaEndpointsDefault{ + _statusCode: code, + } +} + +/* +ListGiteaEndpointsDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type ListGiteaEndpointsDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this list gitea endpoints default response has a 2xx status code +func (o *ListGiteaEndpointsDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this list gitea endpoints default response has a 3xx status code +func (o *ListGiteaEndpointsDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this list gitea endpoints default response has a 4xx status code +func (o *ListGiteaEndpointsDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this list gitea endpoints default response has a 5xx status code +func (o *ListGiteaEndpointsDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this list gitea endpoints default response a status code equal to that given +func (o *ListGiteaEndpointsDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the list gitea endpoints default response +func (o *ListGiteaEndpointsDefault) Code() int { + return o._statusCode +} + +func (o *ListGiteaEndpointsDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/endpoints][%d] ListGiteaEndpoints default %s", o._statusCode, payload) +} + +func (o *ListGiteaEndpointsDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /gitea/endpoints][%d] ListGiteaEndpoints default %s", o._statusCode, payload) +} + +func (o *ListGiteaEndpointsDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *ListGiteaEndpointsDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/endpoints/list_github_endpoints_responses.go b/client/endpoints/list_github_endpoints_responses.go index f7b10a1c..33485f9b 100644 --- a/client/endpoints/list_github_endpoints_responses.go +++ b/client/endpoints/list_github_endpoints_responses.go @@ -51,7 +51,7 @@ func NewListGithubEndpointsOK() *ListGithubEndpointsOK { /* ListGithubEndpointsOK describes a response with status code 200, with default header values. -GithubEndpoints +ForgeEndpoints */ type ListGithubEndpointsOK struct { Payload garm_params.ForgeEndpoints diff --git a/client/endpoints/update_gitea_endpoint_parameters.go b/client/endpoints/update_gitea_endpoint_parameters.go new file mode 100644 index 00000000..bfd18e2e --- /dev/null +++ b/client/endpoints/update_gitea_endpoint_parameters.go @@ -0,0 +1,173 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + garm_params "github.com/cloudbase/garm/params" +) + +// NewUpdateGiteaEndpointParams creates a new UpdateGiteaEndpointParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewUpdateGiteaEndpointParams() *UpdateGiteaEndpointParams { + return &UpdateGiteaEndpointParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewUpdateGiteaEndpointParamsWithTimeout creates a new UpdateGiteaEndpointParams object +// with the ability to set a timeout on a request. +func NewUpdateGiteaEndpointParamsWithTimeout(timeout time.Duration) *UpdateGiteaEndpointParams { + return &UpdateGiteaEndpointParams{ + timeout: timeout, + } +} + +// NewUpdateGiteaEndpointParamsWithContext creates a new UpdateGiteaEndpointParams object +// with the ability to set a context for a request. +func NewUpdateGiteaEndpointParamsWithContext(ctx context.Context) *UpdateGiteaEndpointParams { + return &UpdateGiteaEndpointParams{ + Context: ctx, + } +} + +// NewUpdateGiteaEndpointParamsWithHTTPClient creates a new UpdateGiteaEndpointParams object +// with the ability to set a custom HTTPClient for a request. +func NewUpdateGiteaEndpointParamsWithHTTPClient(client *http.Client) *UpdateGiteaEndpointParams { + return &UpdateGiteaEndpointParams{ + HTTPClient: client, + } +} + +/* +UpdateGiteaEndpointParams contains all the parameters to send to the API endpoint + + for the update gitea endpoint operation. + + Typically these are written to a http.Request. +*/ +type UpdateGiteaEndpointParams struct { + + /* Body. + + Parameters used when updating a Gitea endpoint. + */ + Body garm_params.UpdateGiteaEndpointParams + + /* Name. + + The name of the Gitea endpoint. + */ + Name string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the update gitea endpoint params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *UpdateGiteaEndpointParams) WithDefaults() *UpdateGiteaEndpointParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the update gitea endpoint params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *UpdateGiteaEndpointParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) WithTimeout(timeout time.Duration) *UpdateGiteaEndpointParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) WithContext(ctx context.Context) *UpdateGiteaEndpointParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) WithHTTPClient(client *http.Client) *UpdateGiteaEndpointParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) WithBody(body garm_params.UpdateGiteaEndpointParams) *UpdateGiteaEndpointParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) SetBody(body garm_params.UpdateGiteaEndpointParams) { + o.Body = body +} + +// WithName adds the name to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) WithName(name string) *UpdateGiteaEndpointParams { + o.SetName(name) + return o +} + +// SetName adds the name to the update gitea endpoint params +func (o *UpdateGiteaEndpointParams) SetName(name string) { + o.Name = name +} + +// WriteToRequest writes these params to a swagger request +func (o *UpdateGiteaEndpointParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + // path param name + if err := r.SetPathParam("name", o.Name); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/endpoints/update_gitea_endpoint_responses.go b/client/endpoints/update_gitea_endpoint_responses.go new file mode 100644 index 00000000..052f45fa --- /dev/null +++ b/client/endpoints/update_gitea_endpoint_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package endpoints + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// UpdateGiteaEndpointReader is a Reader for the UpdateGiteaEndpoint structure. +type UpdateGiteaEndpointReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *UpdateGiteaEndpointReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewUpdateGiteaEndpointOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewUpdateGiteaEndpointDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewUpdateGiteaEndpointOK creates a UpdateGiteaEndpointOK with default headers values +func NewUpdateGiteaEndpointOK() *UpdateGiteaEndpointOK { + return &UpdateGiteaEndpointOK{} +} + +/* +UpdateGiteaEndpointOK describes a response with status code 200, with default header values. + +ForgeEndpoint +*/ +type UpdateGiteaEndpointOK struct { + Payload garm_params.ForgeEndpoint +} + +// IsSuccess returns true when this update gitea endpoint o k response has a 2xx status code +func (o *UpdateGiteaEndpointOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this update gitea endpoint o k response has a 3xx status code +func (o *UpdateGiteaEndpointOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this update gitea endpoint o k response has a 4xx status code +func (o *UpdateGiteaEndpointOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this update gitea endpoint o k response has a 5xx status code +func (o *UpdateGiteaEndpointOK) IsServerError() bool { + return false +} + +// IsCode returns true when this update gitea endpoint o k response a status code equal to that given +func (o *UpdateGiteaEndpointOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the update gitea endpoint o k response +func (o *UpdateGiteaEndpointOK) Code() int { + return 200 +} + +func (o *UpdateGiteaEndpointOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /gitea/endpoints/{name}][%d] updateGiteaEndpointOK %s", 200, payload) +} + +func (o *UpdateGiteaEndpointOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /gitea/endpoints/{name}][%d] updateGiteaEndpointOK %s", 200, payload) +} + +func (o *UpdateGiteaEndpointOK) GetPayload() garm_params.ForgeEndpoint { + return o.Payload +} + +func (o *UpdateGiteaEndpointOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewUpdateGiteaEndpointDefault creates a UpdateGiteaEndpointDefault with default headers values +func NewUpdateGiteaEndpointDefault(code int) *UpdateGiteaEndpointDefault { + return &UpdateGiteaEndpointDefault{ + _statusCode: code, + } +} + +/* +UpdateGiteaEndpointDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type UpdateGiteaEndpointDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this update gitea endpoint default response has a 2xx status code +func (o *UpdateGiteaEndpointDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this update gitea endpoint default response has a 3xx status code +func (o *UpdateGiteaEndpointDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this update gitea endpoint default response has a 4xx status code +func (o *UpdateGiteaEndpointDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this update gitea endpoint default response has a 5xx status code +func (o *UpdateGiteaEndpointDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this update gitea endpoint default response a status code equal to that given +func (o *UpdateGiteaEndpointDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the update gitea endpoint default response +func (o *UpdateGiteaEndpointDefault) Code() int { + return o._statusCode +} + +func (o *UpdateGiteaEndpointDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /gitea/endpoints/{name}][%d] UpdateGiteaEndpoint default %s", o._statusCode, payload) +} + +func (o *UpdateGiteaEndpointDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /gitea/endpoints/{name}][%d] UpdateGiteaEndpoint default %s", o._statusCode, payload) +} + +func (o *UpdateGiteaEndpointDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *UpdateGiteaEndpointDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/endpoints/update_github_endpoint_responses.go b/client/endpoints/update_github_endpoint_responses.go index 969d6bbd..27cd4a71 100644 --- a/client/endpoints/update_github_endpoint_responses.go +++ b/client/endpoints/update_github_endpoint_responses.go @@ -51,7 +51,7 @@ func NewUpdateGithubEndpointOK() *UpdateGithubEndpointOK { /* UpdateGithubEndpointOK describes a response with status code 200, with default header values. -GithubEndpoint +ForgeEndpoint */ type UpdateGithubEndpointOK struct { Payload garm_params.ForgeEndpoint diff --git a/cmd/garm-cli/cmd/gitea.go b/cmd/garm-cli/cmd/gitea.go new file mode 100644 index 00000000..10d086bd --- /dev/null +++ b/cmd/garm-cli/cmd/gitea.go @@ -0,0 +1,21 @@ +package cmd + +import "github.com/spf13/cobra" + +// giteaCmd represents the the gitea command. This command has a set +// of subcommands that allow configuring and managing Gitea endpoints +// and credentials. +var giteaCmd = &cobra.Command{ + Use: "gitea", + Aliases: []string{"gt"}, + SilenceUsage: true, + Short: "Manage Gitea resources", + Long: `Manage Gitea related resources. + +This command allows you to configure and manage Gitea endpoints and credentials`, + Run: nil, +} + +func init() { + rootCmd.AddCommand(giteaCmd) +} diff --git a/cmd/garm-cli/cmd/gitea_credentials.go b/cmd/garm-cli/cmd/gitea_credentials.go new file mode 100644 index 00000000..c744c8a7 --- /dev/null +++ b/cmd/garm-cli/cmd/gitea_credentials.go @@ -0,0 +1,317 @@ +// 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 cmd + +import ( + "fmt" + "strconv" + + "github.com/jedib0t/go-pretty/v6/table" + "github.com/spf13/cobra" + + apiClientCreds "github.com/cloudbase/garm/client/credentials" + "github.com/cloudbase/garm/cmd/garm-cli/common" + "github.com/cloudbase/garm/params" +) + +// giteaCredentialsCmd represents the gitea credentials command +var giteaCredentialsCmd = &cobra.Command{ + Use: "credentials", + Aliases: []string{"creds"}, + Short: "Manage gitea credentials", + Long: `Manage Gitea credentials stored in GARM. + +This command allows you to add, update, list and delete Gitea credentials.`, + Run: nil, +} + +var giteaCredentialsListCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List configured gitea credentials", + Long: `List the names of the gitea personal access tokens available to the garm.`, + SilenceUsage: true, + RunE: func(_ *cobra.Command, _ []string) error { + if needsInit { + return errNeedsInitError + } + + listCredsReq := apiClientCreds.NewListGiteaCredentialsParams() + response, err := apiCli.Credentials.ListGiteaCredentials(listCredsReq, authToken) + if err != nil { + return err + } + formatGiteaCredentials(response.Payload) + return nil + }, +} + +var giteaCredentialsShowCmd = &cobra.Command{ + Use: "show", + Aliases: []string{"get"}, + Short: "Show details of a configured gitea credential", + Long: `Show the details of a configured gitea credential.`, + SilenceUsage: true, + RunE: func(_ *cobra.Command, args []string) error { + if needsInit { + return errNeedsInitError + } + + if len(args) < 1 { + return fmt.Errorf("missing required argument: credential ID") + } + + credID, err := strconv.ParseInt(args[0], 10, 64) + if err != nil { + return fmt.Errorf("invalid credential ID: %s", args[0]) + } + showCredsReq := apiClientCreds.NewGetGiteaCredentialsParams().WithID(credID) + response, err := apiCli.Credentials.GetGiteaCredentials(showCredsReq, authToken) + if err != nil { + return err + } + formatOneGiteaCredential(response.Payload) + return nil + }, +} + +var giteaCredentialsUpdateCmd = &cobra.Command{ + Use: "update", + Short: "Update a gitea credential", + Long: "Update a gitea credential", + SilenceUsage: true, + RunE: func(_ *cobra.Command, args []string) error { + if needsInit { + return errNeedsInitError + } + + if len(args) < 1 { + return fmt.Errorf("missing required argument: credential ID") + } + + if len(args) > 1 { + return fmt.Errorf("too many arguments") + } + + credID, err := strconv.ParseInt(args[0], 10, 64) + if err != nil { + return fmt.Errorf("invalid credential ID: %s", args[0]) + } + + updateParams, err := parseGiteaCredentialsUpdateParams() + if err != nil { + return err + } + + updateCredsReq := apiClientCreds.NewUpdateGiteaCredentialsParams().WithID(credID) + updateCredsReq.Body = updateParams + + response, err := apiCli.Credentials.UpdateGiteaCredentials(updateCredsReq, authToken) + if err != nil { + return err + } + formatOneGiteaCredential(response.Payload) + return nil + }, +} + +var giteaCredentialsDeleteCmd = &cobra.Command{ + Use: "delete", + Aliases: []string{"remove", "rm"}, + Short: "Delete a gitea credential", + Long: "Delete a gitea credential", + SilenceUsage: true, + RunE: func(_ *cobra.Command, args []string) error { + if needsInit { + return errNeedsInitError + } + + if len(args) < 1 { + return fmt.Errorf("missing required argument: credential ID") + } + + if len(args) > 1 { + return fmt.Errorf("too many arguments") + } + + credID, err := strconv.ParseInt(args[0], 10, 64) + if err != nil { + return fmt.Errorf("invalid credential ID: %s", args[0]) + } + + deleteCredsReq := apiClientCreds.NewDeleteGiteaCredentialsParams().WithID(credID) + if err := apiCli.Credentials.DeleteGiteaCredentials(deleteCredsReq, authToken); err != nil { + return err + } + return nil + }, +} + +var giteaCredentialsAddCmd = &cobra.Command{ + Use: "add", + Short: "Add a gitea credential", + Long: "Add a gitea credential", + SilenceUsage: true, + RunE: func(_ *cobra.Command, args []string) error { + if needsInit { + return errNeedsInitError + } + + if len(args) > 0 { + return fmt.Errorf("too many arguments") + } + + addParams, err := parseGiteaCredentialsAddParams() + if err != nil { + return err + } + + addCredsReq := apiClientCreds.NewCreateGiteaCredentialsParams() + addCredsReq.Body = addParams + + response, err := apiCli.Credentials.CreateGiteaCredentials(addCredsReq, authToken) + if err != nil { + return err + } + formatOneGiteaCredential(response.Payload) + return nil + }, +} + +func init() { + giteaCredentialsUpdateCmd.Flags().StringVar(&credentialsName, "name", "", "Name of the credential") + giteaCredentialsUpdateCmd.Flags().StringVar(&credentialsDescription, "description", "", "Description of the credential") + giteaCredentialsUpdateCmd.Flags().StringVar(&credentialsOAuthToken, "pat-oauth-token", "", "If the credential is a personal access token, the OAuth token") + + giteaCredentialsListCmd.Flags().BoolVarP(&long, "long", "l", false, "Include additional info.") + + giteaCredentialsAddCmd.Flags().StringVar(&credentialsName, "name", "", "Name of the credential") + giteaCredentialsAddCmd.Flags().StringVar(&credentialsDescription, "description", "", "Description of the credential") + giteaCredentialsAddCmd.Flags().StringVar(&credentialsOAuthToken, "pat-oauth-token", "", "If the credential is a personal access token, the OAuth token") + giteaCredentialsAddCmd.Flags().StringVar(&credentialsType, "auth-type", "", "The type of the credential") + giteaCredentialsAddCmd.Flags().StringVar(&credentialsEndpoint, "endpoint", "", "The endpoint to associate the credential with") + + giteaCredentialsAddCmd.MarkFlagRequired("name") + giteaCredentialsAddCmd.MarkFlagRequired("auth-type") + giteaCredentialsAddCmd.MarkFlagRequired("description") + giteaCredentialsAddCmd.MarkFlagRequired("endpoint") + + giteaCredentialsCmd.AddCommand( + giteaCredentialsListCmd, + giteaCredentialsShowCmd, + giteaCredentialsUpdateCmd, + giteaCredentialsDeleteCmd, + giteaCredentialsAddCmd, + ) + giteaCmd.AddCommand(giteaCredentialsCmd) +} + +func parseGiteaCredentialsAddParams() (ret params.CreateGiteaCredentialsParams, err error) { + ret.Name = credentialsName + ret.Description = credentialsDescription + ret.AuthType = params.ForgeAuthType(credentialsType) + ret.Endpoint = credentialsEndpoint + switch ret.AuthType { + case params.ForgeAuthTypePAT: + ret.PAT.OAuth2Token = credentialsOAuthToken + default: + return params.CreateGiteaCredentialsParams{}, fmt.Errorf("invalid auth type: %s (supported are: pat)", credentialsType) + } + + return ret, nil +} + +func parseGiteaCredentialsUpdateParams() (params.UpdateGiteaCredentialsParams, error) { + var updateParams params.UpdateGiteaCredentialsParams + + if credentialsName != "" { + updateParams.Name = &credentialsName + } + + if credentialsDescription != "" { + updateParams.Description = &credentialsDescription + } + + if credentialsOAuthToken != "" { + if updateParams.PAT == nil { + updateParams.PAT = ¶ms.GithubPAT{} + } + updateParams.PAT.OAuth2Token = credentialsOAuthToken + } + + return updateParams, nil +} + +func formatGiteaCredentials(creds []params.ForgeCredentials) { + if outputFormat == common.OutputFormatJSON { + printAsJSON(creds) + return + } + t := table.NewWriter() + header := table.Row{"ID", "Name", "Description", "Base URL", "API URL", "Type"} + if long { + header = append(header, "Created At", "Updated At") + } + t.AppendHeader(header) + for _, val := range creds { + row := table.Row{val.ID, val.Name, val.Description, val.BaseURL, val.APIBaseURL, val.AuthType} + if long { + row = append(row, val.CreatedAt, val.UpdatedAt) + } + t.AppendRow(row) + t.AppendSeparator() + } + fmt.Println(t.Render()) +} + +func formatOneGiteaCredential(cred params.ForgeCredentials) { + if outputFormat == common.OutputFormatJSON { + printAsJSON(cred) + return + } + t := table.NewWriter() + header := table.Row{"Field", "Value"} + t.AppendHeader(header) + + t.AppendRow(table.Row{"ID", cred.ID}) + t.AppendRow(table.Row{"Created At", cred.CreatedAt}) + t.AppendRow(table.Row{"Updated At", cred.UpdatedAt}) + t.AppendRow(table.Row{"Name", cred.Name}) + t.AppendRow(table.Row{"Description", cred.Description}) + t.AppendRow(table.Row{"Base URL", cred.BaseURL}) + t.AppendRow(table.Row{"API URL", cred.APIBaseURL}) + t.AppendRow(table.Row{"Type", cred.AuthType}) + t.AppendRow(table.Row{"Endpoint", cred.Endpoint.Name}) + + if len(cred.Repositories) > 0 { + t.AppendRow(table.Row{"", ""}) + for _, repo := range cred.Repositories { + t.AppendRow(table.Row{"Repositories", repo.String()}) + } + } + + if len(cred.Organizations) > 0 { + t.AppendRow(table.Row{"", ""}) + for _, org := range cred.Organizations { + t.AppendRow(table.Row{"Organizations", org.Name}) + } + } + + t.SetColumnConfigs([]table.ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, AutoMerge: false, WidthMax: 100}, + }) + fmt.Println(t.Render()) +} diff --git a/cmd/garm-cli/cmd/gitea_endpoints.go b/cmd/garm-cli/cmd/gitea_endpoints.go new file mode 100644 index 00000000..d3504f17 --- /dev/null +++ b/cmd/garm-cli/cmd/gitea_endpoints.go @@ -0,0 +1,218 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + + apiClientEndpoints "github.com/cloudbase/garm/client/endpoints" + "github.com/cloudbase/garm/params" +) + +var giteaEndpointCmd = &cobra.Command{ + Use: "endpoint", + SilenceUsage: true, + Short: "Manage Gitea endpoints", + Long: `Manage Gitea endpoints. + +This command allows you to configure and manage Gitea endpoints`, + Run: nil, +} + +var giteaEndpointListCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + SilenceUsage: true, + Short: "List Gitea endpoints", + Long: `List all configured Gitea endpoints.`, + RunE: func(_ *cobra.Command, _ []string) error { + if needsInit { + return errNeedsInitError + } + + newListReq := apiClientEndpoints.NewListGiteaEndpointsParams() + response, err := apiCli.Endpoints.ListGiteaEndpoints(newListReq, authToken) + if err != nil { + return err + } + formatEndpoints(response.Payload) + return nil + }, +} + +var giteaEndpointShowCmd = &cobra.Command{ + Use: "show", + Aliases: []string{"get"}, + SilenceUsage: true, + Short: "Show Gitea endpoint", + Long: `Show details of a Gitea endpoint.`, + RunE: func(_ *cobra.Command, args []string) error { + if needsInit { + return errNeedsInitError + } + if len(args) == 0 { + return fmt.Errorf("requires an endpoint name") + } + if len(args) > 1 { + return fmt.Errorf("too many arguments") + } + + newShowReq := apiClientEndpoints.NewGetGiteaEndpointParams() + newShowReq.Name = args[0] + response, err := apiCli.Endpoints.GetGiteaEndpoint(newShowReq, authToken) + if err != nil { + return err + } + formatOneEndpoint(response.Payload) + return nil + }, +} + +var giteaEndpointCreateCmd = &cobra.Command{ + Use: "create", + SilenceUsage: true, + Short: "Create Gitea endpoint", + Long: `Create a new Gitea endpoint.`, + RunE: func(_ *cobra.Command, _ []string) error { + if needsInit { + return errNeedsInitError + } + + createParams, err := parseGiteaCreateParams() + if err != nil { + return err + } + + newCreateReq := apiClientEndpoints.NewCreateGiteaEndpointParams() + newCreateReq.Body = createParams + + response, err := apiCli.Endpoints.CreateGiteaEndpoint(newCreateReq, authToken) + if err != nil { + return err + } + formatOneEndpoint(response.Payload) + return nil + }, +} + +var giteaEndpointDeleteCmd = &cobra.Command{ + Use: "delete", + Aliases: []string{"remove", "rm"}, + SilenceUsage: true, + Short: "Delete Gitea endpoint", + Long: "Delete a Gitea endpoint", + RunE: func(_ *cobra.Command, args []string) error { + if needsInit { + return errNeedsInitError + } + if len(args) == 0 { + return fmt.Errorf("requires an endpoint name") + } + if len(args) > 1 { + return fmt.Errorf("too many arguments") + } + + newDeleteReq := apiClientEndpoints.NewDeleteGiteaEndpointParams() + newDeleteReq.Name = args[0] + if err := apiCli.Endpoints.DeleteGiteaEndpoint(newDeleteReq, authToken); err != nil { + return err + } + return nil + }, +} + +var giteaEndpointUpdateCmd = &cobra.Command{ + Use: "update", + Short: "Update Gitea endpoint", + Long: "Update a Gitea endpoint", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + if needsInit { + return errNeedsInitError + } + if len(args) == 0 { + return fmt.Errorf("requires an endpoint name") + } + if len(args) > 1 { + return fmt.Errorf("too many arguments") + } + + updateParams := params.UpdateGiteaEndpointParams{} + + if cmd.Flags().Changed("ca-cert-path") { + cert, err := parseAndReadCABundle() + if err != nil { + return err + } + updateParams.CACertBundle = cert + } + + if cmd.Flags().Changed("description") { + updateParams.Description = &endpointDescription + } + + if cmd.Flags().Changed("base-url") { + updateParams.BaseURL = &endpointBaseURL + } + + if cmd.Flags().Changed("api-base-url") { + updateParams.APIBaseURL = &endpointAPIBaseURL + } + + newEndpointUpdateReq := apiClientEndpoints.NewUpdateGiteaEndpointParams() + newEndpointUpdateReq.Name = args[0] + newEndpointUpdateReq.Body = updateParams + + response, err := apiCli.Endpoints.UpdateGiteaEndpoint(newEndpointUpdateReq, authToken) + if err != nil { + return err + } + formatOneEndpoint(response.Payload) + return nil + }, +} + +func init() { + giteaEndpointCreateCmd.Flags().StringVar(&endpointName, "name", "", "Name of the Gitea endpoint") + giteaEndpointCreateCmd.Flags().StringVar(&endpointDescription, "description", "", "Description for the github endpoint") + giteaEndpointCreateCmd.Flags().StringVar(&endpointBaseURL, "base-url", "", "Base URL of the Gitea endpoint") + giteaEndpointCreateCmd.Flags().StringVar(&endpointAPIBaseURL, "api-base-url", "", "API Base URL of the Gitea endpoint") + giteaEndpointCreateCmd.Flags().StringVar(&endpointCACertPath, "ca-cert-path", "", "CA Cert Path of the Gitea endpoint") + + giteaEndpointListCmd.Flags().BoolVarP(&long, "long", "l", false, "Include additional info.") + + giteaEndpointCreateCmd.MarkFlagRequired("name") + giteaEndpointCreateCmd.MarkFlagRequired("base-url") + giteaEndpointCreateCmd.MarkFlagRequired("api-base-url") + + giteaEndpointUpdateCmd.Flags().StringVar(&endpointDescription, "description", "", "Description for the gitea endpoint") + giteaEndpointUpdateCmd.Flags().StringVar(&endpointBaseURL, "base-url", "", "Base URL of the Gitea endpoint") + giteaEndpointUpdateCmd.Flags().StringVar(&endpointAPIBaseURL, "api-base-url", "", "API Base URL of the Gitea endpoint") + giteaEndpointUpdateCmd.Flags().StringVar(&endpointCACertPath, "ca-cert-path", "", "CA Cert Path of the Gitea endpoint") + + giteaEndpointCmd.AddCommand( + giteaEndpointListCmd, + giteaEndpointShowCmd, + giteaEndpointCreateCmd, + giteaEndpointDeleteCmd, + giteaEndpointUpdateCmd, + ) + + giteaCmd.AddCommand(giteaEndpointCmd) +} + +func parseGiteaCreateParams() (params.CreateGiteaEndpointParams, error) { + certBundleBytes, err := parseAndReadCABundle() + if err != nil { + return params.CreateGiteaEndpointParams{}, err + } + + ret := params.CreateGiteaEndpointParams{ + Name: endpointName, + BaseURL: endpointBaseURL, + APIBaseURL: endpointAPIBaseURL, + Description: endpointDescription, + CACertBundle: certBundleBytes, + } + return ret, nil +} diff --git a/cmd/garm-cli/cmd/github_credentials.go b/cmd/garm-cli/cmd/github_credentials.go index fb3853d8..ae2374f6 100644 --- a/cmd/garm-cli/cmd/github_credentials.go +++ b/cmd/garm-cli/cmd/github_credentials.go @@ -344,7 +344,7 @@ func parseCredentialsUpdateParams() (params.UpdateGithubCredentialsParams, error return updateParams, nil } -func formatGithubCredentials(creds []params.GithubCredentials) { +func formatGithubCredentials(creds []params.ForgeCredentials) { if outputFormat == common.OutputFormatJSON { printAsJSON(creds) return @@ -366,7 +366,7 @@ func formatGithubCredentials(creds []params.GithubCredentials) { fmt.Println(t.Render()) } -func formatOneGithubCredential(cred params.GithubCredentials) { +func formatOneGithubCredential(cred params.ForgeCredentials) { if outputFormat == common.OutputFormatJSON { printAsJSON(cred) return diff --git a/cmd/garm-cli/cmd/github_endpoints.go b/cmd/garm-cli/cmd/github_endpoints.go index f119a1a2..fbdca86c 100644 --- a/cmd/garm-cli/cmd/github_endpoints.go +++ b/cmd/garm-cli/cmd/github_endpoints.go @@ -145,7 +145,7 @@ var githubEndpointUpdateCmd = &cobra.Command{ updateParams := params.UpdateGithubEndpointParams{} if cmd.Flags().Changed("ca-cert-path") { - cert, err := parseReadAndParsCABundle() + cert, err := parseAndReadCABundle() if err != nil { return err } @@ -213,7 +213,7 @@ func init() { githubCmd.AddCommand(githubEndpointCmd) } -func parseReadAndParsCABundle() ([]byte, error) { +func parseAndReadCABundle() ([]byte, error) { if endpointCACertPath == "" { return nil, nil } @@ -236,7 +236,7 @@ func parseReadAndParsCABundle() ([]byte, error) { } func parseCreateParams() (params.CreateGithubEndpointParams, error) { - certBundleBytes, err := parseReadAndParsCABundle() + certBundleBytes, err := parseAndReadCABundle() if err != nil { return params.CreateGithubEndpointParams{}, err } @@ -287,7 +287,9 @@ func formatOneEndpoint(endpoint params.ForgeEndpoint) { t.AppendRow([]interface{}{"Created At", endpoint.CreatedAt}) t.AppendRow([]interface{}{"Updated At", endpoint.UpdatedAt}) t.AppendRow([]interface{}{"Base URL", endpoint.BaseURL}) - t.AppendRow([]interface{}{"Upload URL", endpoint.UploadBaseURL}) + if endpoint.UploadBaseURL != "" { + t.AppendRow([]interface{}{"Upload URL", endpoint.UploadBaseURL}) + } t.AppendRow([]interface{}{"API Base URL", endpoint.APIBaseURL}) if len(endpoint.CACertBundle) > 0 { t.AppendRow([]interface{}{"CA Cert Bundle", string(endpoint.CACertBundle)}) diff --git a/cmd/garm-cli/cmd/repository.go b/cmd/garm-cli/cmd/repository.go index 1c453836..b24cf039 100644 --- a/cmd/garm-cli/cmd/repository.go +++ b/cmd/garm-cli/cmd/repository.go @@ -31,6 +31,7 @@ var ( repoName string repoWebhookSecret string repoCreds string + forgeType string randomWebhookSecret bool insecureRepoWebhook bool keepRepoWebhook bool @@ -169,6 +170,7 @@ var repoAddCmd = &cobra.Command{ Name: repoName, WebhookSecret: repoWebhookSecret, CredentialsName: repoCreds, + ForgeType: params.EndpointType(forgeType), PoolBalancerType: params.PoolBalancerType(poolBalancerType), } response, err := apiCli.Repositories.CreateRepo(newRepoReq, authToken) @@ -309,6 +311,7 @@ func init() { repoAddCmd.Flags().StringVar(&repoOwner, "owner", "", "The owner of this repository") repoAddCmd.Flags().StringVar(&poolBalancerType, "pool-balancer-type", string(params.PoolBalancerTypeRoundRobin), "The balancing strategy to use when creating runners in pools matching requested labels.") repoAddCmd.Flags().StringVar(&repoName, "name", "", "The name of the repository") + repoAddCmd.Flags().StringVar(&forgeType, "forge-type", string(params.GithubEndpointType), "The forge type of the repository. Supported values: github, gitea.") repoAddCmd.Flags().StringVar(&repoWebhookSecret, "webhook-secret", "", "The webhook secret for this repository") repoAddCmd.Flags().StringVar(&repoCreds, "credentials", "", "Credentials name. See credentials list.") repoAddCmd.Flags().BoolVar(&randomWebhookSecret, "random-webhook-secret", false, "Generate a random webhook secret for this repository.") @@ -360,7 +363,7 @@ func formatRepositories(repos []params.Repository) { } t.AppendHeader(header) for _, val := range repos { - row := table.Row{val.ID, val.Owner, val.Name, val.Endpoint.Name, val.CredentialsName, val.GetBalancerType(), val.PoolManagerStatus.IsRunning} + row := table.Row{val.ID, val.Owner, val.Name, val.Endpoint.Name, val.GetCredentialsName(), val.GetBalancerType(), val.PoolManagerStatus.IsRunning} if long { row = append(row, val.CreatedAt, val.UpdatedAt) } @@ -386,7 +389,7 @@ func formatOneRepository(repo params.Repository) { t.AppendRow(table.Row{"Name", repo.Name}) t.AppendRow(table.Row{"Endpoint", repo.Endpoint.Name}) t.AppendRow(table.Row{"Pool balancer type", repo.GetBalancerType()}) - t.AppendRow(table.Row{"Credentials", repo.CredentialsName}) + t.AppendRow(table.Row{"Credentials", repo.GetCredentialsName()}) t.AppendRow(table.Row{"Pool manager running", repo.PoolManagerStatus.IsRunning}) if !repo.PoolManagerStatus.IsRunning { t.AppendRow(table.Row{"Failure reason", repo.PoolManagerStatus.FailureReason}) diff --git a/cmd/garm/main.go b/cmd/garm/main.go index 20f34eba..f37248d3 100644 --- a/cmd/garm/main.go +++ b/cmd/garm/main.go @@ -384,6 +384,7 @@ func main() { slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to stop provider worker") } + slog.InfoContext(ctx, "shutting down http server") shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 60*time.Second) defer shutdownCancel() if err := srv.Shutdown(shutdownCtx); err != nil { diff --git a/database/common/mocks/Store.go b/database/common/mocks/Store.go index 53b90720..30f1774c 100644 --- a/database/common/mocks/Store.go +++ b/database/common/mocks/Store.go @@ -180,23 +180,79 @@ func (_m *Store) CreateEntityScaleSet(_a0 context.Context, entity params.ForgeEn return r0, r1 } +// CreateGiteaCredentials provides a mock function with given fields: ctx, param +func (_m *Store) CreateGiteaCredentials(ctx context.Context, param params.CreateGiteaCredentialsParams) (params.ForgeCredentials, error) { + ret := _m.Called(ctx, param) + + if len(ret) == 0 { + panic("no return value specified for CreateGiteaCredentials") + } + + var r0 params.ForgeCredentials + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, params.CreateGiteaCredentialsParams) (params.ForgeCredentials, error)); ok { + return rf(ctx, param) + } + if rf, ok := ret.Get(0).(func(context.Context, params.CreateGiteaCredentialsParams) params.ForgeCredentials); ok { + r0 = rf(ctx, param) + } else { + r0 = ret.Get(0).(params.ForgeCredentials) + } + + if rf, ok := ret.Get(1).(func(context.Context, params.CreateGiteaCredentialsParams) error); ok { + r1 = rf(ctx, param) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CreateGiteaEndpoint provides a mock function with given fields: _a0, param +func (_m *Store) CreateGiteaEndpoint(_a0 context.Context, param params.CreateGiteaEndpointParams) (params.ForgeEndpoint, error) { + ret := _m.Called(_a0, param) + + if len(ret) == 0 { + panic("no return value specified for CreateGiteaEndpoint") + } + + var r0 params.ForgeEndpoint + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, params.CreateGiteaEndpointParams) (params.ForgeEndpoint, error)); ok { + return rf(_a0, param) + } + if rf, ok := ret.Get(0).(func(context.Context, params.CreateGiteaEndpointParams) params.ForgeEndpoint); ok { + r0 = rf(_a0, param) + } else { + r0 = ret.Get(0).(params.ForgeEndpoint) + } + + if rf, ok := ret.Get(1).(func(context.Context, params.CreateGiteaEndpointParams) error); ok { + r1 = rf(_a0, param) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CreateGithubCredentials provides a mock function with given fields: ctx, param -func (_m *Store) CreateGithubCredentials(ctx context.Context, param params.CreateGithubCredentialsParams) (params.GithubCredentials, error) { +func (_m *Store) CreateGithubCredentials(ctx context.Context, param params.CreateGithubCredentialsParams) (params.ForgeCredentials, error) { ret := _m.Called(ctx, param) if len(ret) == 0 { panic("no return value specified for CreateGithubCredentials") } - var r0 params.GithubCredentials + var r0 params.ForgeCredentials var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.CreateGithubCredentialsParams) (params.GithubCredentials, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.CreateGithubCredentialsParams) (params.ForgeCredentials, error)); ok { return rf(ctx, param) } - if rf, ok := ret.Get(0).(func(context.Context, params.CreateGithubCredentialsParams) params.GithubCredentials); ok { + if rf, ok := ret.Get(0).(func(context.Context, params.CreateGithubCredentialsParams) params.ForgeCredentials); ok { r0 = rf(ctx, param) } else { - r0 = ret.Get(0).(params.GithubCredentials) + r0 = ret.Get(0).(params.ForgeCredentials) } if rf, ok := ret.Get(1).(func(context.Context, params.CreateGithubCredentialsParams) error); ok { @@ -320,9 +376,9 @@ func (_m *Store) CreateOrganization(ctx context.Context, name string, credential return r0, r1 } -// CreateRepository provides a mock function with given fields: ctx, owner, name, credentialsName, webhookSecret, poolBalancerType -func (_m *Store) CreateRepository(ctx context.Context, owner string, name string, credentialsName string, webhookSecret string, poolBalancerType params.PoolBalancerType) (params.Repository, error) { - ret := _m.Called(ctx, owner, name, credentialsName, webhookSecret, poolBalancerType) +// CreateRepository provides a mock function with given fields: ctx, owner, name, credentials, webhookSecret, poolBalancerType +func (_m *Store) CreateRepository(ctx context.Context, owner string, name string, credentials params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (params.Repository, error) { + ret := _m.Called(ctx, owner, name, credentials, webhookSecret, poolBalancerType) if len(ret) == 0 { panic("no return value specified for CreateRepository") @@ -330,17 +386,17 @@ func (_m *Store) CreateRepository(ctx context.Context, owner string, name string var r0 params.Repository var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, params.PoolBalancerType) (params.Repository, error)); ok { - return rf(ctx, owner, name, credentialsName, webhookSecret, poolBalancerType) + if rf, ok := ret.Get(0).(func(context.Context, string, string, params.ForgeCredentials, string, params.PoolBalancerType) (params.Repository, error)); ok { + return rf(ctx, owner, name, credentials, webhookSecret, poolBalancerType) } - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, params.PoolBalancerType) params.Repository); ok { - r0 = rf(ctx, owner, name, credentialsName, webhookSecret, poolBalancerType) + if rf, ok := ret.Get(0).(func(context.Context, string, string, params.ForgeCredentials, string, params.PoolBalancerType) params.Repository); ok { + r0 = rf(ctx, owner, name, credentials, webhookSecret, poolBalancerType) } else { r0 = ret.Get(0).(params.Repository) } - if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string, params.PoolBalancerType) error); ok { - r1 = rf(ctx, owner, name, credentialsName, webhookSecret, poolBalancerType) + if rf, ok := ret.Get(1).(func(context.Context, string, string, params.ForgeCredentials, string, params.PoolBalancerType) error); ok { + r1 = rf(ctx, owner, name, credentials, webhookSecret, poolBalancerType) } else { r1 = ret.Error(1) } @@ -458,6 +514,42 @@ func (_m *Store) DeleteEntityPool(ctx context.Context, entity params.ForgeEntity return r0 } +// DeleteGiteaCredentials provides a mock function with given fields: ctx, id +func (_m *Store) DeleteGiteaCredentials(ctx context.Context, id uint) error { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for DeleteGiteaCredentials") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteGiteaEndpoint provides a mock function with given fields: _a0, name +func (_m *Store) DeleteGiteaEndpoint(_a0 context.Context, name string) error { + ret := _m.Called(_a0, name) + + if len(ret) == 0 { + panic("no return value specified for DeleteGiteaEndpoint") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(_a0, name) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // DeleteGithubCredentials provides a mock function with given fields: ctx, id func (_m *Store) DeleteGithubCredentials(ctx context.Context, id uint) error { ret := _m.Called(ctx, id) @@ -762,23 +854,135 @@ func (_m *Store) GetEntityPool(ctx context.Context, entity params.ForgeEntity, p return r0, r1 } +// GetForgeEntity provides a mock function with given fields: _a0, entityType, entityID +func (_m *Store) GetForgeEntity(_a0 context.Context, entityType params.ForgeEntityType, entityID string) (params.ForgeEntity, error) { + ret := _m.Called(_a0, entityType, entityID) + + if len(ret) == 0 { + panic("no return value specified for GetForgeEntity") + } + + var r0 params.ForgeEntity + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string) (params.ForgeEntity, error)); ok { + return rf(_a0, entityType, entityID) + } + if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string) params.ForgeEntity); ok { + r0 = rf(_a0, entityType, entityID) + } else { + r0 = ret.Get(0).(params.ForgeEntity) + } + + if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntityType, string) error); ok { + r1 = rf(_a0, entityType, entityID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetGiteaCredentials provides a mock function with given fields: ctx, id, detailed +func (_m *Store) GetGiteaCredentials(ctx context.Context, id uint, detailed bool) (params.ForgeCredentials, error) { + ret := _m.Called(ctx, id, detailed) + + if len(ret) == 0 { + panic("no return value specified for GetGiteaCredentials") + } + + var r0 params.ForgeCredentials + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint, bool) (params.ForgeCredentials, error)); ok { + return rf(ctx, id, detailed) + } + if rf, ok := ret.Get(0).(func(context.Context, uint, bool) params.ForgeCredentials); ok { + r0 = rf(ctx, id, detailed) + } else { + r0 = ret.Get(0).(params.ForgeCredentials) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint, bool) error); ok { + r1 = rf(ctx, id, detailed) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetGiteaCredentialsByName provides a mock function with given fields: ctx, name, detailed +func (_m *Store) GetGiteaCredentialsByName(ctx context.Context, name string, detailed bool) (params.ForgeCredentials, error) { + ret := _m.Called(ctx, name, detailed) + + if len(ret) == 0 { + panic("no return value specified for GetGiteaCredentialsByName") + } + + var r0 params.ForgeCredentials + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (params.ForgeCredentials, error)); ok { + return rf(ctx, name, detailed) + } + if rf, ok := ret.Get(0).(func(context.Context, string, bool) params.ForgeCredentials); ok { + r0 = rf(ctx, name, detailed) + } else { + r0 = ret.Get(0).(params.ForgeCredentials) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { + r1 = rf(ctx, name, detailed) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetGiteaEndpoint provides a mock function with given fields: _a0, name +func (_m *Store) GetGiteaEndpoint(_a0 context.Context, name string) (params.ForgeEndpoint, error) { + ret := _m.Called(_a0, name) + + if len(ret) == 0 { + panic("no return value specified for GetGiteaEndpoint") + } + + var r0 params.ForgeEndpoint + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (params.ForgeEndpoint, error)); ok { + return rf(_a0, name) + } + if rf, ok := ret.Get(0).(func(context.Context, string) params.ForgeEndpoint); ok { + r0 = rf(_a0, name) + } else { + r0 = ret.Get(0).(params.ForgeEndpoint) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(_a0, name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetGithubCredentials provides a mock function with given fields: ctx, id, detailed -func (_m *Store) GetGithubCredentials(ctx context.Context, id uint, detailed bool) (params.GithubCredentials, error) { +func (_m *Store) GetGithubCredentials(ctx context.Context, id uint, detailed bool) (params.ForgeCredentials, error) { ret := _m.Called(ctx, id, detailed) if len(ret) == 0 { panic("no return value specified for GetGithubCredentials") } - var r0 params.GithubCredentials + var r0 params.ForgeCredentials var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint, bool) (params.GithubCredentials, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, uint, bool) (params.ForgeCredentials, error)); ok { return rf(ctx, id, detailed) } - if rf, ok := ret.Get(0).(func(context.Context, uint, bool) params.GithubCredentials); ok { + if rf, ok := ret.Get(0).(func(context.Context, uint, bool) params.ForgeCredentials); ok { r0 = rf(ctx, id, detailed) } else { - r0 = ret.Get(0).(params.GithubCredentials) + r0 = ret.Get(0).(params.ForgeCredentials) } if rf, ok := ret.Get(1).(func(context.Context, uint, bool) error); ok { @@ -791,22 +995,22 @@ func (_m *Store) GetGithubCredentials(ctx context.Context, id uint, detailed boo } // GetGithubCredentialsByName provides a mock function with given fields: ctx, name, detailed -func (_m *Store) GetGithubCredentialsByName(ctx context.Context, name string, detailed bool) (params.GithubCredentials, error) { +func (_m *Store) GetGithubCredentialsByName(ctx context.Context, name string, detailed bool) (params.ForgeCredentials, error) { ret := _m.Called(ctx, name, detailed) if len(ret) == 0 { panic("no return value specified for GetGithubCredentialsByName") } - var r0 params.GithubCredentials + var r0 params.ForgeCredentials var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, bool) (params.GithubCredentials, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (params.ForgeCredentials, error)); ok { return rf(ctx, name, detailed) } - if rf, ok := ret.Get(0).(func(context.Context, string, bool) params.GithubCredentials); ok { + if rf, ok := ret.Get(0).(func(context.Context, string, bool) params.ForgeCredentials); ok { r0 = rf(ctx, name, detailed) } else { - r0 = ret.Get(0).(params.GithubCredentials) + r0 = ret.Get(0).(params.ForgeCredentials) } if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { @@ -846,34 +1050,6 @@ func (_m *Store) GetGithubEndpoint(ctx context.Context, name string) (params.For return r0, r1 } -// GetForgeEntity provides a mock function with given fields: _a0, entityType, entityID -func (_m *Store) GetForgeEntity(_a0 context.Context, entityType params.ForgeEntityType, entityID string) (params.ForgeEntity, error) { - ret := _m.Called(_a0, entityType, entityID) - - if len(ret) == 0 { - panic("no return value specified for GetForgeEntity") - } - - var r0 params.ForgeEntity - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string) (params.ForgeEntity, error)); ok { - return rf(_a0, entityType, entityID) - } - if rf, ok := ret.Get(0).(func(context.Context, params.ForgeEntityType, string) params.ForgeEntity); ok { - r0 = rf(_a0, entityType, entityID) - } else { - r0 = ret.Get(0).(params.ForgeEntity) - } - - if rf, ok := ret.Get(1).(func(context.Context, params.ForgeEntityType, string) error); ok { - r1 = rf(_a0, entityType, entityID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetInstanceByName provides a mock function with given fields: ctx, instanceName func (_m *Store) GetInstanceByName(ctx context.Context, instanceName string) (params.Instance, error) { ret := _m.Called(ctx, instanceName) @@ -1498,24 +1674,84 @@ func (_m *Store) ListEntityScaleSets(_a0 context.Context, entity params.ForgeEnt return r0, r1 } +// ListGiteaCredentials provides a mock function with given fields: ctx +func (_m *Store) ListGiteaCredentials(ctx context.Context) ([]params.ForgeCredentials, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ListGiteaCredentials") + } + + var r0 []params.ForgeCredentials + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]params.ForgeCredentials, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []params.ForgeCredentials); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]params.ForgeCredentials) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListGiteaEndpoints provides a mock function with given fields: _a0 +func (_m *Store) ListGiteaEndpoints(_a0 context.Context) ([]params.ForgeEndpoint, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for ListGiteaEndpoints") + } + + var r0 []params.ForgeEndpoint + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]params.ForgeEndpoint, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) []params.ForgeEndpoint); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]params.ForgeEndpoint) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ListGithubCredentials provides a mock function with given fields: ctx -func (_m *Store) ListGithubCredentials(ctx context.Context) ([]params.GithubCredentials, error) { +func (_m *Store) ListGithubCredentials(ctx context.Context) ([]params.ForgeCredentials, error) { ret := _m.Called(ctx) if len(ret) == 0 { panic("no return value specified for ListGithubCredentials") } - var r0 []params.GithubCredentials + var r0 []params.ForgeCredentials var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]params.GithubCredentials, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context) ([]params.ForgeCredentials, error)); ok { return rf(ctx) } - if rf, ok := ret.Get(0).(func(context.Context) []params.GithubCredentials); ok { + if rf, ok := ret.Get(0).(func(context.Context) []params.ForgeCredentials); ok { r0 = rf(ctx) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]params.GithubCredentials) + r0 = ret.Get(0).([]params.ForgeCredentials) } } @@ -1920,23 +2156,79 @@ func (_m *Store) UpdateEntityScaleSet(_a0 context.Context, entity params.ForgeEn return r0, r1 } +// UpdateGiteaCredentials provides a mock function with given fields: ctx, id, param +func (_m *Store) UpdateGiteaCredentials(ctx context.Context, id uint, param params.UpdateGiteaCredentialsParams) (params.ForgeCredentials, error) { + ret := _m.Called(ctx, id, param) + + if len(ret) == 0 { + panic("no return value specified for UpdateGiteaCredentials") + } + + var r0 params.ForgeCredentials + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint, params.UpdateGiteaCredentialsParams) (params.ForgeCredentials, error)); ok { + return rf(ctx, id, param) + } + if rf, ok := ret.Get(0).(func(context.Context, uint, params.UpdateGiteaCredentialsParams) params.ForgeCredentials); ok { + r0 = rf(ctx, id, param) + } else { + r0 = ret.Get(0).(params.ForgeCredentials) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint, params.UpdateGiteaCredentialsParams) error); ok { + r1 = rf(ctx, id, param) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateGiteaEndpoint provides a mock function with given fields: _a0, name, param +func (_m *Store) UpdateGiteaEndpoint(_a0 context.Context, name string, param params.UpdateGiteaEndpointParams) (params.ForgeEndpoint, error) { + ret := _m.Called(_a0, name, param) + + if len(ret) == 0 { + panic("no return value specified for UpdateGiteaEndpoint") + } + + var r0 params.ForgeEndpoint + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, params.UpdateGiteaEndpointParams) (params.ForgeEndpoint, error)); ok { + return rf(_a0, name, param) + } + if rf, ok := ret.Get(0).(func(context.Context, string, params.UpdateGiteaEndpointParams) params.ForgeEndpoint); ok { + r0 = rf(_a0, name, param) + } else { + r0 = ret.Get(0).(params.ForgeEndpoint) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, params.UpdateGiteaEndpointParams) error); ok { + r1 = rf(_a0, name, param) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // UpdateGithubCredentials provides a mock function with given fields: ctx, id, param -func (_m *Store) UpdateGithubCredentials(ctx context.Context, id uint, param params.UpdateGithubCredentialsParams) (params.GithubCredentials, error) { +func (_m *Store) UpdateGithubCredentials(ctx context.Context, id uint, param params.UpdateGithubCredentialsParams) (params.ForgeCredentials, error) { ret := _m.Called(ctx, id, param) if len(ret) == 0 { panic("no return value specified for UpdateGithubCredentials") } - var r0 params.GithubCredentials + var r0 params.ForgeCredentials var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint, params.UpdateGithubCredentialsParams) (params.GithubCredentials, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, uint, params.UpdateGithubCredentialsParams) (params.ForgeCredentials, error)); ok { return rf(ctx, id, param) } - if rf, ok := ret.Get(0).(func(context.Context, uint, params.UpdateGithubCredentialsParams) params.GithubCredentials); ok { + if rf, ok := ret.Get(0).(func(context.Context, uint, params.UpdateGithubCredentialsParams) params.ForgeCredentials); ok { r0 = rf(ctx, id, param) } else { - r0 = ret.Get(0).(params.GithubCredentials) + r0 = ret.Get(0).(params.ForgeCredentials) } if rf, ok := ret.Get(1).(func(context.Context, uint, params.UpdateGithubCredentialsParams) error); ok { diff --git a/database/common/store.go b/database/common/store.go index e5458eaf..1f5b013b 100644 --- a/database/common/store.go +++ b/database/common/store.go @@ -29,16 +29,16 @@ type GithubEndpointStore interface { } type GithubCredentialsStore interface { - CreateGithubCredentials(ctx context.Context, param params.CreateGithubCredentialsParams) (params.GithubCredentials, error) - GetGithubCredentials(ctx context.Context, id uint, detailed bool) (params.GithubCredentials, error) - GetGithubCredentialsByName(ctx context.Context, name string, detailed bool) (params.GithubCredentials, error) - ListGithubCredentials(ctx context.Context) ([]params.GithubCredentials, error) - UpdateGithubCredentials(ctx context.Context, id uint, param params.UpdateGithubCredentialsParams) (params.GithubCredentials, error) + CreateGithubCredentials(ctx context.Context, param params.CreateGithubCredentialsParams) (params.ForgeCredentials, error) + GetGithubCredentials(ctx context.Context, id uint, detailed bool) (params.ForgeCredentials, error) + GetGithubCredentialsByName(ctx context.Context, name string, detailed bool) (params.ForgeCredentials, error) + ListGithubCredentials(ctx context.Context) ([]params.ForgeCredentials, error) + UpdateGithubCredentials(ctx context.Context, id uint, param params.UpdateGithubCredentialsParams) (params.ForgeCredentials, error) DeleteGithubCredentials(ctx context.Context, id uint) error } type RepoStore interface { - CreateRepository(ctx context.Context, owner, name, credentialsName, webhookSecret string, poolBalancerType params.PoolBalancerType) (params.Repository, error) + CreateRepository(ctx context.Context, owner, name string, credentials params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (param params.Repository, err error) GetRepository(ctx context.Context, owner, name, endpointName string) (params.Repository, error) GetRepositoryByID(ctx context.Context, repoID string) (params.Repository, error) ListRepositories(ctx context.Context) ([]params.Repository, error) @@ -152,6 +152,23 @@ type ScaleSetInstanceStore interface { CreateScaleSetInstance(_ context.Context, scaleSetID uint, param params.CreateInstanceParams) (instance params.Instance, err error) } +type GiteaEndpointStore interface { + CreateGiteaEndpoint(_ context.Context, param params.CreateGiteaEndpointParams) (ghEndpoint params.ForgeEndpoint, err error) + ListGiteaEndpoints(_ context.Context) ([]params.ForgeEndpoint, error) + DeleteGiteaEndpoint(_ context.Context, name string) (err error) + GetGiteaEndpoint(_ context.Context, name string) (params.ForgeEndpoint, error) + UpdateGiteaEndpoint(_ context.Context, name string, param params.UpdateGiteaEndpointParams) (ghEndpoint params.ForgeEndpoint, err error) +} + +type GiteaCredentialsStore interface { + CreateGiteaCredentials(ctx context.Context, param params.CreateGiteaCredentialsParams) (gtCreds params.ForgeCredentials, err error) + GetGiteaCredentialsByName(ctx context.Context, name string, detailed bool) (params.ForgeCredentials, error) + GetGiteaCredentials(ctx context.Context, id uint, detailed bool) (params.ForgeCredentials, error) + ListGiteaCredentials(ctx context.Context) ([]params.ForgeCredentials, error) + UpdateGiteaCredentials(ctx context.Context, id uint, param params.UpdateGiteaCredentialsParams) (gtCreds params.ForgeCredentials, err error) + DeleteGiteaCredentials(ctx context.Context, id uint) (err error) +} + //go:generate mockery --name=Store type Store interface { RepoStore @@ -167,6 +184,8 @@ type Store interface { EntityPoolStore ScaleSetsStore ScaleSetInstanceStore + GiteaEndpointStore + GiteaCredentialsStore ControllerInfo() (params.ControllerInfo, error) InitController() (params.ControllerInfo, error) diff --git a/database/common/watcher.go b/database/common/watcher.go index 85df1151..4dc18437 100644 --- a/database/common/watcher.go +++ b/database/common/watcher.go @@ -18,6 +18,7 @@ const ( JobEntityType DatabaseEntityType = "job" ControllerEntityType DatabaseEntityType = "controller" GithubCredentialsEntityType DatabaseEntityType = "github_credentials" // #nosec G101 + GiteaCredentialsEntityType DatabaseEntityType = "gitea_credentials" // #nosec G101 GithubEndpointEntityType DatabaseEntityType = "github_endpoint" ScaleSetEntityType DatabaseEntityType = "scaleset" ) diff --git a/database/sql/enterprise.go b/database/sql/enterprise.go index 414a7aaf..26406ac5 100644 --- a/database/sql/enterprise.go +++ b/database/sql/enterprise.go @@ -45,7 +45,6 @@ func (s *sqlDatabase) CreateEnterprise(ctx context.Context, name, credentialsNam newEnterprise := Enterprise{ Name: name, WebhookSecret: secret, - CredentialsName: credentialsName, PoolBalancerType: poolBalancerType, } err = s.conn.Transaction(func(tx *gorm.DB) error { @@ -57,7 +56,6 @@ func (s *sqlDatabase) CreateEnterprise(ctx context.Context, name, credentialsNam return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") } newEnterprise.CredentialsID = &creds.ID - newEnterprise.CredentialsName = creds.Name newEnterprise.EndpointName = creds.EndpointName q := tx.Create(&newEnterprise) diff --git a/database/sql/enterprise_test.go b/database/sql/enterprise_test.go index 3e8f6493..4971f78f 100644 --- a/database/sql/enterprise_test.go +++ b/database/sql/enterprise_test.go @@ -53,8 +53,8 @@ type EnterpriseTestSuite struct { adminCtx context.Context adminUserID string - testCreds params.GithubCredentials - secondaryTestCreds params.GithubCredentials + testCreds params.ForgeCredentials + secondaryTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint } diff --git a/database/sql/gitea.go b/database/sql/gitea.go new file mode 100644 index 00000000..5ce46663 --- /dev/null +++ b/database/sql/gitea.go @@ -0,0 +1,469 @@ +package sql + +import ( + "context" + "log/slog" + + runnerErrors "github.com/cloudbase/garm-provider-common/errors" + "github.com/cloudbase/garm/auth" + "github.com/cloudbase/garm/database/common" + "github.com/cloudbase/garm/params" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +func (s *sqlDatabase) CreateGiteaEndpoint(_ context.Context, param params.CreateGiteaEndpointParams) (ghEndpoint params.ForgeEndpoint, err error) { + defer func() { + if err == nil { + s.sendNotify(common.GithubEndpointEntityType, common.CreateOperation, ghEndpoint) + } + }() + var endpoint GithubEndpoint + err = s.conn.Transaction(func(tx *gorm.DB) error { + if err := tx.Where("name = ?", param.Name).First(&endpoint).Error; err == nil { + return errors.Wrap(runnerErrors.ErrDuplicateEntity, "github endpoint already exists") + } + endpoint = GithubEndpoint{ + Name: param.Name, + Description: param.Description, + APIBaseURL: param.APIBaseURL, + BaseURL: param.BaseURL, + CACertBundle: param.CACertBundle, + EndpointType: params.GiteaEndpointType, + } + + if err := tx.Create(&endpoint).Error; err != nil { + return errors.Wrap(err, "creating github endpoint") + } + return nil + }) + if err != nil { + return params.ForgeEndpoint{}, errors.Wrap(err, "creating github endpoint") + } + ghEndpoint, err = s.sqlToCommonGithubEndpoint(endpoint) + if err != nil { + return params.ForgeEndpoint{}, errors.Wrap(err, "converting github endpoint") + } + return ghEndpoint, nil +} + +func (s *sqlDatabase) ListGiteaEndpoints(_ context.Context) ([]params.ForgeEndpoint, error) { + var endpoints []GithubEndpoint + err := s.conn.Where("endpoint_type = ?", params.GiteaEndpointType).Find(&endpoints).Error + if err != nil { + return nil, errors.Wrap(err, "fetching github endpoints") + } + + var ret []params.ForgeEndpoint + for _, ep := range endpoints { + commonEp, err := s.sqlToCommonGithubEndpoint(ep) + if err != nil { + return nil, errors.Wrap(err, "converting github endpoint") + } + ret = append(ret, commonEp) + } + return ret, nil +} + +func (s *sqlDatabase) UpdateGiteaEndpoint(_ context.Context, name string, param params.UpdateGiteaEndpointParams) (ghEndpoint params.ForgeEndpoint, err error) { + if name == defaultGithubEndpoint { + return params.ForgeEndpoint{}, runnerErrors.NewBadRequestError("cannot update default endpoint %s", defaultGithubEndpoint) + } + + defer func() { + if err == nil { + s.sendNotify(common.GithubEndpointEntityType, common.UpdateOperation, ghEndpoint) + } + }() + var endpoint GithubEndpoint + err = s.conn.Transaction(func(tx *gorm.DB) error { + if err := tx.Where("name = ? and endpoint_type = ?", name, params.GiteaEndpointType).First(&endpoint).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return errors.Wrap(runnerErrors.ErrNotFound, "gitea endpoint not found") + } + return errors.Wrap(err, "fetching gitea endpoint") + } + if param.APIBaseURL != nil { + endpoint.APIBaseURL = *param.APIBaseURL + } + + if param.BaseURL != nil { + endpoint.BaseURL = *param.BaseURL + } + + if param.CACertBundle != nil { + endpoint.CACertBundle = param.CACertBundle + } + + if param.Description != nil { + endpoint.Description = *param.Description + } + + if err := tx.Save(&endpoint).Error; err != nil { + return errors.Wrap(err, "updating gitea endpoint") + } + + return nil + }) + if err != nil { + return params.ForgeEndpoint{}, errors.Wrap(err, "updating gitea endpoint") + } + ghEndpoint, err = s.sqlToCommonGithubEndpoint(endpoint) + if err != nil { + return params.ForgeEndpoint{}, errors.Wrap(err, "converting gitea endpoint") + } + return ghEndpoint, nil +} + +func (s *sqlDatabase) GetGiteaEndpoint(_ context.Context, name string) (params.ForgeEndpoint, error) { + var endpoint GithubEndpoint + + err := s.conn.Where("name = ? and endpoint_type = ?", name, params.GiteaEndpointType).First(&endpoint).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return params.ForgeEndpoint{}, errors.Wrap(runnerErrors.ErrNotFound, "gitea endpoint not found") + } + return params.ForgeEndpoint{}, errors.Wrap(err, "fetching gitea endpoint") + } + + return s.sqlToCommonGithubEndpoint(endpoint) +} + +func (s *sqlDatabase) DeleteGiteaEndpoint(_ context.Context, name string) (err error) { + if name == defaultGithubEndpoint { + return runnerErrors.NewBadRequestError("cannot delete default endpoint %s", defaultGithubEndpoint) + } + + defer func() { + if err == nil { + s.sendNotify(common.GithubEndpointEntityType, common.DeleteOperation, params.ForgeEndpoint{Name: name}) + } + }() + err = s.conn.Transaction(func(tx *gorm.DB) error { + var endpoint GithubEndpoint + if err := tx.Where("name = ? and endpoint_type = ?", name, params.GiteaEndpointType).First(&endpoint).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } + return errors.Wrap(err, "fetching gitea endpoint") + } + + var credsCount int64 + if err := tx.Model(&GithubCredentials{}).Where("endpoint_name = ?", endpoint.Name).Count(&credsCount).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return errors.Wrap(err, "fetching gitea credentials") + } + } + + var repoCnt int64 + if err := tx.Model(&Repository{}).Where("endpoint_name = ?", endpoint.Name).Count(&repoCnt).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return errors.Wrap(err, "fetching gitea repositories") + } + } + + var orgCnt int64 + if err := tx.Model(&Organization{}).Where("endpoint_name = ?", endpoint.Name).Count(&orgCnt).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return errors.Wrap(err, "fetching gitea organizations") + } + } + + var entCnt int64 + if err := tx.Model(&Enterprise{}).Where("endpoint_name = ?", endpoint.Name).Count(&entCnt).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return errors.Wrap(err, "fetching gitea enterprises") + } + } + + if credsCount > 0 || repoCnt > 0 || orgCnt > 0 || entCnt > 0 { + return errors.New("cannot delete endpoint with associated entities") + } + + if err := tx.Unscoped().Delete(&endpoint).Error; err != nil { + return errors.Wrap(err, "deleting gitea endpoint") + } + return nil + }) + if err != nil { + return errors.Wrap(err, "deleting gitea endpoint") + } + return nil +} + +func (s *sqlDatabase) CreateGiteaCredentials(ctx context.Context, param params.CreateGiteaCredentialsParams) (gtCreds params.ForgeCredentials, err error) { + userID, err := getUIDFromContext(ctx) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "creating github credentials") + } + if param.Endpoint == "" { + return params.ForgeCredentials{}, errors.Wrap(runnerErrors.ErrBadRequest, "endpoint name is required") + } + + defer func() { + if err == nil { + s.sendNotify(common.GiteaCredentialsEntityType, common.CreateOperation, gtCreds) + } + }() + var creds GiteaCredentials + err = s.conn.Transaction(func(tx *gorm.DB) error { + var endpoint GithubEndpoint + if err := tx.Where("name = ? and endpoint_type = ?", param.Endpoint, params.GiteaEndpointType).First(&endpoint).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return errors.Wrap(runnerErrors.ErrNotFound, "github endpoint not found") + } + return errors.Wrap(err, "fetching github endpoint") + } + + if err := tx.Where("name = ? and user_id = ?", param.Name, userID).First(&creds).Error; err == nil { + return errors.Wrap(runnerErrors.ErrDuplicateEntity, "github credentials already exists") + } + + var data []byte + var err error + switch param.AuthType { + case params.ForgeAuthTypePAT: + data, err = s.marshalAndSeal(param.PAT) + case params.ForgeAuthTypeApp: + data, err = s.marshalAndSeal(param.App) + default: + return errors.Wrap(runnerErrors.ErrBadRequest, "invalid auth type") + } + if err != nil { + return errors.Wrap(err, "marshaling and sealing credentials") + } + + creds = GiteaCredentials{ + Name: param.Name, + Description: param.Description, + EndpointName: &endpoint.Name, + AuthType: param.AuthType, + Payload: data, + UserID: &userID, + } + + if err := tx.Create(&creds).Error; err != nil { + return errors.Wrap(err, "creating github credentials") + } + // Skip making an extra query. + creds.Endpoint = endpoint + + return nil + }) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "creating github credentials") + } + gtCreds, err = s.sqlGiteaToCommonForgeCredentials(creds) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "converting github credentials") + } + return gtCreds, nil +} + +func (s *sqlDatabase) getGiteaCredentialsByName(ctx context.Context, tx *gorm.DB, name string, detailed bool) (GiteaCredentials, error) { + var creds GiteaCredentials + q := tx.Preload("Endpoint") + + if detailed { + q = q. + Preload("Repositories"). + Preload("Organizations") + } + + userID, err := getUIDFromContext(ctx) + if err != nil { + return GiteaCredentials{}, errors.Wrap(err, "fetching gitea credentials") + } + q = q.Where("user_id = ?", userID) + + err = q.Where("name = ?", name).First(&creds).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return GiteaCredentials{}, errors.Wrap(runnerErrors.ErrNotFound, "gitea credentials not found") + } + return GiteaCredentials{}, errors.Wrap(err, "fetching gitea credentials") + } + + return creds, nil +} + +func (s *sqlDatabase) GetGiteaCredentialsByName(ctx context.Context, name string, detailed bool) (params.ForgeCredentials, error) { + creds, err := s.getGiteaCredentialsByName(ctx, s.conn, name, detailed) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "fetching gitea credentials") + } + + return s.sqlGiteaToCommonForgeCredentials(creds) +} + +func (s *sqlDatabase) GetGiteaCredentials(ctx context.Context, id uint, detailed bool) (params.ForgeCredentials, error) { + var creds GiteaCredentials + q := s.conn.Preload("Endpoint") + + if detailed { + q = q. + Preload("Repositories"). + Preload("Organizations") + } + + if !auth.IsAdmin(ctx) { + userID, err := getUIDFromContext(ctx) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "fetching gitea credentials") + } + q = q.Where("user_id = ?", userID) + } + + err := q.Where("id = ?", id).First(&creds).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return params.ForgeCredentials{}, errors.Wrap(runnerErrors.ErrNotFound, "gitea credentials not found") + } + return params.ForgeCredentials{}, errors.Wrap(err, "fetching gitea credentials") + } + + return s.sqlGiteaToCommonForgeCredentials(creds) +} + +func (s *sqlDatabase) ListGiteaCredentials(ctx context.Context) ([]params.ForgeCredentials, error) { + q := s.conn.Preload("Endpoint") + if !auth.IsAdmin(ctx) { + userID, err := getUIDFromContext(ctx) + if err != nil { + return nil, errors.Wrap(err, "fetching gitea credentials") + } + q = q.Where("user_id = ?", userID) + } + + var creds []GiteaCredentials + err := q.Preload("Endpoint").Find(&creds).Error + if err != nil { + return nil, errors.Wrap(err, "fetching gitea credentials") + } + + var ret []params.ForgeCredentials + for _, c := range creds { + commonCreds, err := s.sqlGiteaToCommonForgeCredentials(c) + if err != nil { + return nil, errors.Wrap(err, "converting gitea credentials") + } + ret = append(ret, commonCreds) + } + return ret, nil +} + +func (s *sqlDatabase) UpdateGiteaCredentials(ctx context.Context, id uint, param params.UpdateGiteaCredentialsParams) (gtCreds params.ForgeCredentials, err error) { + defer func() { + if err == nil { + s.sendNotify(common.GiteaCredentialsEntityType, common.UpdateOperation, gtCreds) + } + }() + var creds GiteaCredentials + err = s.conn.Transaction(func(tx *gorm.DB) error { + q := tx.Preload("Endpoint") + if !auth.IsAdmin(ctx) { + userID, err := getUIDFromContext(ctx) + if err != nil { + return errors.Wrap(err, "updating gitea credentials") + } + q = q.Where("user_id = ?", userID) + } + + if err := q.Where("id = ?", id).First(&creds).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return errors.Wrap(runnerErrors.ErrNotFound, "gitea credentials not found") + } + return errors.Wrap(err, "fetching gitea credentials") + } + + if param.Name != nil { + creds.Name = *param.Name + } + if param.Description != nil { + creds.Description = *param.Description + } + + var data []byte + var err error + switch creds.AuthType { + case params.ForgeAuthTypePAT: + if param.PAT != nil { + data, err = s.marshalAndSeal(param.PAT) + } + default: + return errors.Wrap(runnerErrors.ErrBadRequest, "invalid auth type") + } + + if err != nil { + return errors.Wrap(err, "marshaling and sealing credentials") + } + if len(data) > 0 { + creds.Payload = data + } + + if err := tx.Save(&creds).Error; err != nil { + return errors.Wrap(err, "updating gitea credentials") + } + return nil + }) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "updating gitea credentials") + } + + gtCreds, err = s.sqlGiteaToCommonForgeCredentials(creds) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "converting gitea credentials") + } + return gtCreds, nil +} + +func (s *sqlDatabase) DeleteGiteaCredentials(ctx context.Context, id uint) (err error) { + var creds GiteaCredentials + defer func() { + if err == nil { + forgeCreds, innerErr := s.sqlGiteaToCommonForgeCredentials(creds) + if innerErr != nil { + slog.ErrorContext(ctx, "converting gitea credentials", "error", innerErr) + } + if creds.ID == 0 || creds.Name == "" { + return + } + s.sendNotify(common.GiteaCredentialsEntityType, common.DeleteOperation, forgeCreds) + } + }() + err = s.conn.Transaction(func(tx *gorm.DB) error { + q := tx.Where("id = ?", id). + Preload("Repositories"). + Preload("Organizations") + if !auth.IsAdmin(ctx) { + userID, err := getUIDFromContext(ctx) + if err != nil { + return errors.Wrap(err, "deleting gitea credentials") + } + q = q.Where("user_id = ?", userID) + } + + err := q.First(&creds).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } + return errors.Wrap(err, "fetching gitea credentials") + } + + if len(creds.Repositories) > 0 { + return errors.Wrap(runnerErrors.ErrBadRequest, "cannot delete credentials with repositories") + } + if len(creds.Organizations) > 0 { + return errors.Wrap(runnerErrors.ErrBadRequest, "cannot delete credentials with organizations") + } + if err := tx.Unscoped().Delete(&creds).Error; err != nil { + return errors.Wrap(err, "deleting gitea credentials") + } + return nil + }) + if err != nil { + return errors.Wrap(err, "deleting gitea credentials") + } + return nil +} diff --git a/database/sql/github.go b/database/sql/github.go index 861c824c..d2c05244 100644 --- a/database/sql/github.go +++ b/database/sql/github.go @@ -17,12 +17,10 @@ package sql import ( "context" - "github.com/google/uuid" "github.com/pkg/errors" "gorm.io/gorm" runnerErrors "github.com/cloudbase/garm-provider-common/errors" - "github.com/cloudbase/garm-provider-common/util" "github.com/cloudbase/garm/auth" "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/params" @@ -32,89 +30,6 @@ const ( defaultGithubEndpoint string = "github.com" ) -func (s *sqlDatabase) sqlToCommonGithubCredentials(creds GithubCredentials) (params.GithubCredentials, error) { - if len(creds.Payload) == 0 { - return params.GithubCredentials{}, errors.New("empty credentials payload") - } - data, err := util.Unseal(creds.Payload, []byte(s.cfg.Passphrase)) - if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "unsealing credentials") - } - - ep, err := s.sqlToCommonGithubEndpoint(creds.Endpoint) - if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "converting github endpoint") - } - - commonCreds := params.GithubCredentials{ - ID: creds.ID, - Name: creds.Name, - Description: creds.Description, - APIBaseURL: creds.Endpoint.APIBaseURL, - BaseURL: creds.Endpoint.BaseURL, - UploadBaseURL: creds.Endpoint.UploadBaseURL, - CABundle: creds.Endpoint.CACertBundle, - AuthType: creds.AuthType, - CreatedAt: creds.CreatedAt, - UpdatedAt: creds.UpdatedAt, - Endpoint: ep, - CredentialsPayload: data, - } - - for _, repo := range creds.Repositories { - commonRepo, err := s.sqlToCommonRepository(repo, false) - if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "converting github repository") - } - commonCreds.Repositories = append(commonCreds.Repositories, commonRepo) - } - - for _, org := range creds.Organizations { - commonOrg, err := s.sqlToCommonOrganization(org, false) - if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "converting github organization") - } - commonCreds.Organizations = append(commonCreds.Organizations, commonOrg) - } - - for _, ent := range creds.Enterprises { - commonEnt, err := s.sqlToCommonEnterprise(ent, false) - if err != nil { - return params.GithubCredentials{}, errors.Wrapf(err, "converting github enterprise: %s", ent.Name) - } - commonCreds.Enterprises = append(commonCreds.Enterprises, commonEnt) - } - - return commonCreds, nil -} - -func (s *sqlDatabase) sqlToCommonGithubEndpoint(ep GithubEndpoint) (params.ForgeEndpoint, error) { - return params.ForgeEndpoint{ - Name: ep.Name, - Description: ep.Description, - APIBaseURL: ep.APIBaseURL, - BaseURL: ep.BaseURL, - UploadBaseURL: ep.UploadBaseURL, - CACertBundle: ep.CACertBundle, - CreatedAt: ep.CreatedAt, - EndpointType: ep.EndpointType, - UpdatedAt: ep.UpdatedAt, - }, nil -} - -func getUIDFromContext(ctx context.Context) (uuid.UUID, error) { - userID := auth.UserID(ctx) - if userID == "" { - return uuid.Nil, errors.Wrap(runnerErrors.ErrUnauthorized, "getting UID from context") - } - - asUUID, err := uuid.Parse(userID) - if err != nil { - return uuid.Nil, errors.Wrap(runnerErrors.ErrUnauthorized, "parsing UID from context") - } - return asUUID, nil -} - func (s *sqlDatabase) CreateGithubEndpoint(_ context.Context, param params.CreateGithubEndpointParams) (ghEndpoint params.ForgeEndpoint, err error) { defer func() { if err == nil { @@ -133,6 +48,7 @@ func (s *sqlDatabase) CreateGithubEndpoint(_ context.Context, param params.Creat BaseURL: param.BaseURL, UploadBaseURL: param.UploadBaseURL, CACertBundle: param.CACertBundle, + EndpointType: params.GithubEndpointType, } if err := tx.Create(&endpoint).Error; err != nil { @@ -152,7 +68,7 @@ func (s *sqlDatabase) CreateGithubEndpoint(_ context.Context, param params.Creat func (s *sqlDatabase) ListGithubEndpoints(_ context.Context) ([]params.ForgeEndpoint, error) { var endpoints []GithubEndpoint - err := s.conn.Find(&endpoints).Error + err := s.conn.Where("endpoint_type = ?", params.GithubEndpointType).Find(&endpoints).Error if err != nil { return nil, errors.Wrap(err, "fetching github endpoints") } @@ -180,7 +96,7 @@ func (s *sqlDatabase) UpdateGithubEndpoint(_ context.Context, name string, param }() var endpoint GithubEndpoint err = s.conn.Transaction(func(tx *gorm.DB) error { - if err := tx.Where("name = ?", name).First(&endpoint).Error; err != nil { + if err := tx.Where("name = ? and endpoint_type = ?", name, params.GithubEndpointType).First(&endpoint).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.Wrap(runnerErrors.ErrNotFound, "github endpoint not found") } @@ -225,7 +141,7 @@ func (s *sqlDatabase) UpdateGithubEndpoint(_ context.Context, name string, param func (s *sqlDatabase) GetGithubEndpoint(_ context.Context, name string) (params.ForgeEndpoint, error) { var endpoint GithubEndpoint - err := s.conn.Where("name = ?", name).First(&endpoint).Error + err := s.conn.Where("name = ? and endpoint_type = ?", name, params.GithubEndpointType).First(&endpoint).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return params.ForgeEndpoint{}, errors.Wrap(runnerErrors.ErrNotFound, "github endpoint not found") @@ -248,7 +164,7 @@ func (s *sqlDatabase) DeleteGithubEndpoint(_ context.Context, name string) (err }() err = s.conn.Transaction(func(tx *gorm.DB) error { var endpoint GithubEndpoint - if err := tx.Where("name = ?", name).First(&endpoint).Error; err != nil { + if err := tx.Where("name = ? and endpoint_type = ?", name, params.GithubEndpointType).First(&endpoint).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil } @@ -298,13 +214,13 @@ func (s *sqlDatabase) DeleteGithubEndpoint(_ context.Context, name string) (err return nil } -func (s *sqlDatabase) CreateGithubCredentials(ctx context.Context, param params.CreateGithubCredentialsParams) (ghCreds params.GithubCredentials, err error) { +func (s *sqlDatabase) CreateGithubCredentials(ctx context.Context, param params.CreateGithubCredentialsParams) (ghCreds params.ForgeCredentials, err error) { userID, err := getUIDFromContext(ctx) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "creating github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "creating github credentials") } if param.Endpoint == "" { - return params.GithubCredentials{}, errors.Wrap(runnerErrors.ErrBadRequest, "endpoint name is required") + return params.ForgeCredentials{}, errors.Wrap(runnerErrors.ErrBadRequest, "endpoint name is required") } defer func() { @@ -315,7 +231,7 @@ func (s *sqlDatabase) CreateGithubCredentials(ctx context.Context, param params. var creds GithubCredentials err = s.conn.Transaction(func(tx *gorm.DB) error { var endpoint GithubEndpoint - if err := tx.Where("name = ?", param.Endpoint).First(&endpoint).Error; err != nil { + if err := tx.Where("name = ? and endpoint_type = ?", param.Endpoint, params.GithubEndpointType).First(&endpoint).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.Wrap(runnerErrors.ErrNotFound, "github endpoint not found") } @@ -358,11 +274,11 @@ func (s *sqlDatabase) CreateGithubCredentials(ctx context.Context, param params. return nil }) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "creating github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "creating github credentials") } - ghCreds, err = s.sqlToCommonGithubCredentials(creds) + ghCreds, err = s.sqlToCommonForgeCredentials(creds) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "converting github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "converting github credentials") } return ghCreds, nil } @@ -395,16 +311,16 @@ func (s *sqlDatabase) getGithubCredentialsByName(ctx context.Context, tx *gorm.D return creds, nil } -func (s *sqlDatabase) GetGithubCredentialsByName(ctx context.Context, name string, detailed bool) (params.GithubCredentials, error) { +func (s *sqlDatabase) GetGithubCredentialsByName(ctx context.Context, name string, detailed bool) (params.ForgeCredentials, error) { creds, err := s.getGithubCredentialsByName(ctx, s.conn, name, detailed) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "fetching github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "fetching github credentials") } - return s.sqlToCommonGithubCredentials(creds) + return s.sqlToCommonForgeCredentials(creds) } -func (s *sqlDatabase) GetGithubCredentials(ctx context.Context, id uint, detailed bool) (params.GithubCredentials, error) { +func (s *sqlDatabase) GetGithubCredentials(ctx context.Context, id uint, detailed bool) (params.ForgeCredentials, error) { var creds GithubCredentials q := s.conn.Preload("Endpoint") @@ -418,7 +334,7 @@ func (s *sqlDatabase) GetGithubCredentials(ctx context.Context, id uint, detaile if !auth.IsAdmin(ctx) { userID, err := getUIDFromContext(ctx) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "fetching github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "fetching github credentials") } q = q.Where("user_id = ?", userID) } @@ -426,15 +342,15 @@ func (s *sqlDatabase) GetGithubCredentials(ctx context.Context, id uint, detaile err := q.Where("id = ?", id).First(&creds).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return params.GithubCredentials{}, errors.Wrap(runnerErrors.ErrNotFound, "github credentials not found") + return params.ForgeCredentials{}, errors.Wrap(runnerErrors.ErrNotFound, "github credentials not found") } - return params.GithubCredentials{}, errors.Wrap(err, "fetching github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "fetching github credentials") } - return s.sqlToCommonGithubCredentials(creds) + return s.sqlToCommonForgeCredentials(creds) } -func (s *sqlDatabase) ListGithubCredentials(ctx context.Context) ([]params.GithubCredentials, error) { +func (s *sqlDatabase) ListGithubCredentials(ctx context.Context) ([]params.ForgeCredentials, error) { q := s.conn.Preload("Endpoint") if !auth.IsAdmin(ctx) { userID, err := getUIDFromContext(ctx) @@ -450,9 +366,9 @@ func (s *sqlDatabase) ListGithubCredentials(ctx context.Context) ([]params.Githu return nil, errors.Wrap(err, "fetching github credentials") } - var ret []params.GithubCredentials + var ret []params.ForgeCredentials for _, c := range creds { - commonCreds, err := s.sqlToCommonGithubCredentials(c) + commonCreds, err := s.sqlToCommonForgeCredentials(c) if err != nil { return nil, errors.Wrap(err, "converting github credentials") } @@ -461,7 +377,7 @@ func (s *sqlDatabase) ListGithubCredentials(ctx context.Context) ([]params.Githu return ret, nil } -func (s *sqlDatabase) UpdateGithubCredentials(ctx context.Context, id uint, param params.UpdateGithubCredentialsParams) (ghCreds params.GithubCredentials, err error) { +func (s *sqlDatabase) UpdateGithubCredentials(ctx context.Context, id uint, param params.UpdateGithubCredentialsParams) (ghCreds params.ForgeCredentials, err error) { defer func() { if err == nil { s.sendNotify(common.GithubCredentialsEntityType, common.UpdateOperation, ghCreds) @@ -530,12 +446,12 @@ func (s *sqlDatabase) UpdateGithubCredentials(ctx context.Context, id uint, para return nil }) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "updating github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "updating github credentials") } - ghCreds, err = s.sqlToCommonGithubCredentials(creds) + ghCreds, err = s.sqlToCommonForgeCredentials(creds) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "converting github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "converting github credentials") } return ghCreds, nil } @@ -544,7 +460,7 @@ func (s *sqlDatabase) DeleteGithubCredentials(ctx context.Context, id uint) (err var name string defer func() { if err == nil { - s.sendNotify(common.GithubCredentialsEntityType, common.DeleteOperation, params.GithubCredentials{ID: id, Name: name}) + s.sendNotify(common.GithubCredentialsEntityType, common.DeleteOperation, params.ForgeCredentials{ID: id, Name: name}) } }() err = s.conn.Transaction(func(tx *gorm.DB) error { diff --git a/database/sql/github_test.go b/database/sql/github_test.go index e46d963d..49de9aa3 100644 --- a/database/sql/github_test.go +++ b/database/sql/github_test.go @@ -533,7 +533,7 @@ func (s *GithubTestSuite) TestDeleteCredentialsFailsIfReposOrgsOrEntitiesUseIt() s.Require().NoError(err) s.Require().NotNil(creds) - repo, err := s.db.CreateRepository(ctx, "test-owner", "test-repo", creds.Name, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + repo, err := s.db.CreateRepository(ctx, "test-owner", "test-repo", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotNil(repo) diff --git a/database/sql/models.go b/database/sql/models.go index 2accccc4..0ff2d8f4 100644 --- a/database/sql/models.go +++ b/database/sql/models.go @@ -1,17 +1,3 @@ -// 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 ( @@ -46,6 +32,22 @@ func (b *Base) BeforeCreate(_ *gorm.DB) error { return nil } +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 Tag struct { Base @@ -152,11 +154,12 @@ type RepositoryEvent struct { type Repository struct { Base - CredentialsName string - CredentialsID *uint `gorm:"index"` Credentials GithubCredentials `gorm:"foreignKey:CredentialsID;constraint:OnDelete:SET NULL"` + GiteaCredentialsID *uint `gorm:"index"` + GiteaCredentials GiteaCredentials `gorm:"foreignKey:GiteaCredentialsID;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 @@ -184,11 +187,12 @@ type OrganizationEvent struct { type Organization struct { Base - CredentialsName string - CredentialsID *uint `gorm:"index"` Credentials GithubCredentials `gorm:"foreignKey:CredentialsID;constraint:OnDelete:SET NULL"` + GiteaCredentialsID *uint `gorm:"index"` + GiteaCredentials GiteaCredentials `gorm:"foreignKey:GiteaCredentialsID;constraint:OnDelete:SET NULL"` + Name string `gorm:"index:idx_org_name_nocase,collate:nocase"` WebhookSecret []byte Pools []Pool `gorm:"foreignKey:OrgID"` @@ -216,8 +220,6 @@ type EnterpriseEvent struct { type Enterprise struct { Base - CredentialsName string - CredentialsID *uint `gorm:"index"` Credentials GithubCredentials `gorm:"foreignKey:CredentialsID;constraint:OnDelete:SET NULL"` @@ -300,22 +302,6 @@ type User struct { 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"` @@ -381,7 +367,7 @@ type GithubEndpoint struct { UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` - EndpointType params.EndpointType + EndpointType params.EndpointType `gorm:"index:idx_endpoint_type"` Description string `gorm:"type:text"` APIBaseURL string `gorm:"type:text collate nocase"` @@ -408,3 +394,21 @@ type GithubCredentials struct { Organizations []Organization `gorm:"foreignKey:CredentialsID"` Enterprises []Enterprise `gorm:"foreignKey:CredentialsID"` } + +type GiteaCredentials struct { + gorm.Model + + Name string `gorm:"index:idx_gitea_credentials,unique;type:varchar(64) collate nocase"` + UserID *uuid.UUID `gorm:"index:idx_gitea_credentials,unique"` + User User `gorm:"foreignKey:UserID"` + + Description string `gorm:"type:text"` + AuthType params.ForgeAuthType `gorm:"index"` + Payload []byte `gorm:"type:longblob"` + + Endpoint GithubEndpoint `gorm:"foreignKey:EndpointName"` + EndpointName *string `gorm:"index"` + + Repositories []Repository `gorm:"foreignKey:GiteaCredentialsID"` + Organizations []Organization `gorm:"foreignKey:GiteaCredentialsID"` +} diff --git a/database/sql/organizations.go b/database/sql/organizations.go index 07ce32d8..bf270445 100644 --- a/database/sql/organizations.go +++ b/database/sql/organizations.go @@ -46,7 +46,6 @@ func (s *sqlDatabase) CreateOrganization(ctx context.Context, name, credentialsN newOrg := Organization{ Name: name, WebhookSecret: secret, - CredentialsName: credentialsName, PoolBalancerType: poolBalancerType, } @@ -59,7 +58,6 @@ func (s *sqlDatabase) CreateOrganization(ctx context.Context, name, credentialsN return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") } newOrg.CredentialsID = &creds.ID - newOrg.CredentialsName = creds.Name newOrg.EndpointName = creds.EndpointName q := tx.Create(&newOrg) @@ -166,7 +164,6 @@ func (s *sqlDatabase) UpdateOrganization(ctx context.Context, orgID string, para } if param.CredentialsName != "" { - org.CredentialsName = param.CredentialsName creds, err = s.getGithubCredentialsByName(ctx, tx, param.CredentialsName, false) if err != nil { return errors.Wrap(err, "fetching credentials") diff --git a/database/sql/organizations_test.go b/database/sql/organizations_test.go index 030a3abe..a7ad23b4 100644 --- a/database/sql/organizations_test.go +++ b/database/sql/organizations_test.go @@ -53,8 +53,8 @@ type OrgTestSuite struct { adminCtx context.Context adminUserID string - testCreds params.GithubCredentials - secondaryTestCreds params.GithubCredentials + testCreds params.ForgeCredentials + secondaryTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint } diff --git a/database/sql/pools_test.go b/database/sql/pools_test.go index 758dcacd..dfb82510 100644 --- a/database/sql/pools_test.go +++ b/database/sql/pools_test.go @@ -211,7 +211,7 @@ func (s *PoolsTestSuite) TestEntityPoolOperations() { ep := garmTesting.CreateDefaultGithubEndpoint(s.ctx, s.Store, s.T()) creds := garmTesting.CreateTestGithubCredentials(s.ctx, "test-creds", s.Store, s.T(), ep) s.T().Cleanup(func() { s.Store.DeleteGithubCredentials(s.ctx, creds.ID) }) - repo, err := s.Store.CreateRepository(s.ctx, "test-owner", "test-repo", creds.Name, "test-secret", params.PoolBalancerTypeRoundRobin) + repo, err := s.Store.CreateRepository(s.ctx, "test-owner", "test-repo", creds, "test-secret", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotEmpty(repo.ID) s.T().Cleanup(func() { s.Store.DeleteRepository(s.ctx, repo.ID) }) @@ -291,7 +291,7 @@ func (s *PoolsTestSuite) TestListEntityInstances() { ep := garmTesting.CreateDefaultGithubEndpoint(s.ctx, s.Store, s.T()) creds := garmTesting.CreateTestGithubCredentials(s.ctx, "test-creds", s.Store, s.T(), ep) s.T().Cleanup(func() { s.Store.DeleteGithubCredentials(s.ctx, creds.ID) }) - repo, err := s.Store.CreateRepository(s.ctx, "test-owner", "test-repo", creds.Name, "test-secret", params.PoolBalancerTypeRoundRobin) + repo, err := s.Store.CreateRepository(s.ctx, "test-owner", "test-repo", creds, "test-secret", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotEmpty(repo.ID) s.T().Cleanup(func() { s.Store.DeleteRepository(s.ctx, repo.ID) }) diff --git a/database/sql/repositories.go b/database/sql/repositories.go index 6b744163..d7419070 100644 --- a/database/sql/repositories.go +++ b/database/sql/repositories.go @@ -29,7 +29,7 @@ import ( "github.com/cloudbase/garm/params" ) -func (s *sqlDatabase) CreateRepository(ctx context.Context, owner, name, credentialsName, webhookSecret string, poolBalancerType params.PoolBalancerType) (param params.Repository, err error) { +func (s *sqlDatabase) CreateRepository(ctx context.Context, owner, name string, credentials params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (param params.Repository, err error) { defer func() { if err == nil { s.sendNotify(common.RepositoryEntityType, common.CreateOperation, param) @@ -51,32 +51,32 @@ func (s *sqlDatabase) CreateRepository(ctx context.Context, owner, name, credent PoolBalancerType: poolBalancerType, } err = s.conn.Transaction(func(tx *gorm.DB) error { - creds, err := s.getGithubCredentialsByName(ctx, tx, credentialsName, false) - if err != nil { - return errors.Wrap(err, "creating repository") + switch credentials.ForgeType { + case params.GithubEndpointType: + newRepo.CredentialsID = &credentials.ID + case params.GiteaEndpointType: + newRepo.GiteaCredentialsID = &credentials.ID + default: + return errors.Wrap(runnerErrors.ErrBadRequest, "unsupported credentials type") } - if creds.EndpointName == nil { - return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") - } - newRepo.CredentialsID = &creds.ID - newRepo.CredentialsName = creds.Name - newRepo.EndpointName = creds.EndpointName + newRepo.EndpointName = &credentials.Endpoint.Name q := tx.Create(&newRepo) if q.Error != nil { return errors.Wrap(q.Error, "creating repository") } - - newRepo.Credentials = creds - newRepo.Endpoint = creds.Endpoint - return nil }) if err != nil { return params.Repository{}, errors.Wrap(err, "creating repository") } - param, err = s.sqlToCommonRepository(newRepo, true) + repo, err := s.getRepoByID(ctx, s.conn, newRepo.ID.String(), "Endpoint", "Credentials", "GiteaCredentials", "Credentials.Endpoint", "GiteaCredentials.Endpoint") + if err != nil { + return params.Repository{}, errors.Wrap(err, "creating repository") + } + + param, err = s.sqlToCommonRepository(repo, true) if err != nil { return params.Repository{}, errors.Wrap(err, "creating repository") } @@ -102,7 +102,9 @@ func (s *sqlDatabase) ListRepositories(_ context.Context) ([]params.Repository, var repos []Repository q := s.conn. Preload("Credentials"). + Preload("GiteaCredentials"). Preload("Credentials.Endpoint"). + Preload("GiteaCredentials.Endpoint"). Preload("Endpoint"). Find(&repos) if q.Error != nil { @@ -122,7 +124,7 @@ func (s *sqlDatabase) ListRepositories(_ context.Context) ([]params.Repository, } func (s *sqlDatabase) DeleteRepository(ctx context.Context, repoID string) (err error) { - repo, err := s.getRepoByID(ctx, s.conn, repoID, "Endpoint", "Credentials", "Credentials.Endpoint") + repo, err := s.getRepoByID(ctx, s.conn, repoID, "Endpoint", "Credentials", "Credentials.Endpoint", "GiteaCredentials", "GiteaCredentials.Endpoint") if err != nil { return errors.Wrap(err, "fetching repo") } @@ -165,7 +167,6 @@ func (s *sqlDatabase) UpdateRepository(ctx context.Context, repoID string, param } if param.CredentialsName != "" { - repo.CredentialsName = param.CredentialsName creds, err = s.getGithubCredentialsByName(ctx, tx, param.CredentialsName, false) if err != nil { return errors.Wrap(err, "fetching credentials") @@ -203,7 +204,7 @@ func (s *sqlDatabase) UpdateRepository(ctx context.Context, repoID string, param return params.Repository{}, errors.Wrap(err, "saving repo") } - repo, err = s.getRepoByID(ctx, s.conn, repoID, "Endpoint", "Credentials", "Credentials.Endpoint") + repo, err = s.getRepoByID(ctx, s.conn, repoID, "Endpoint", "Credentials", "Credentials.Endpoint", "GiteaCredentials", "GiteaCredentials.Endpoint") if err != nil { return params.Repository{}, errors.Wrap(err, "updating enterprise") } @@ -216,7 +217,7 @@ func (s *sqlDatabase) UpdateRepository(ctx context.Context, repoID string, param } func (s *sqlDatabase) GetRepositoryByID(ctx context.Context, repoID string) (params.Repository, error) { - repo, err := s.getRepoByID(ctx, s.conn, repoID, "Pools", "Credentials", "Endpoint", "Credentials.Endpoint") + repo, err := s.getRepoByID(ctx, s.conn, repoID, "Pools", "Credentials", "Endpoint", "Credentials.Endpoint", "GiteaCredentials", "GiteaCredentials.Endpoint") if err != nil { return params.Repository{}, errors.Wrap(err, "fetching repo") } @@ -234,6 +235,8 @@ func (s *sqlDatabase) getRepo(_ context.Context, owner, name, endpointName strin q := s.conn.Where("name = ? COLLATE NOCASE and owner = ? COLLATE NOCASE and endpoint_name = ? COLLATE NOCASE", name, owner, endpointName). Preload("Credentials"). Preload("Credentials.Endpoint"). + Preload("GiteaCredentials"). + Preload("GiteaCredentials.Endpoint"). Preload("Endpoint"). First(&repo) diff --git a/database/sql/repositories_test.go b/database/sql/repositories_test.go index f43b9357..73104a2f 100644 --- a/database/sql/repositories_test.go +++ b/database/sql/repositories_test.go @@ -58,8 +58,8 @@ type RepoTestSuite struct { adminCtx context.Context adminUserID string - testCreds params.GithubCredentials - secondaryTestCreds params.GithubCredentials + testCreds params.ForgeCredentials + secondaryTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint } @@ -119,7 +119,7 @@ func (s *RepoTestSuite) SetupTest() { adminCtx, fmt.Sprintf("test-owner-%d", i), fmt.Sprintf("test-repo-%d", i), - s.testCreds.Name, + s.testCreds, fmt.Sprintf("test-webhook-secret-%d", i), params.PoolBalancerTypeRoundRobin, ) @@ -204,7 +204,7 @@ func (s *RepoTestSuite) TestCreateRepository() { s.adminCtx, s.Fixtures.CreateRepoParams.Owner, s.Fixtures.CreateRepoParams.Name, - s.Fixtures.CreateRepoParams.CredentialsName, + s.testCreds, s.Fixtures.CreateRepoParams.WebhookSecret, params.PoolBalancerTypeRoundRobin, ) @@ -238,7 +238,7 @@ func (s *RepoTestSuite) TestCreateRepositoryInvalidDBPassphrase() { s.adminCtx, s.Fixtures.CreateRepoParams.Owner, s.Fixtures.CreateRepoParams.Name, - s.Fixtures.CreateRepoParams.CredentialsName, + s.testCreds, s.Fixtures.CreateRepoParams.WebhookSecret, params.PoolBalancerTypeRoundRobin, ) @@ -267,7 +267,7 @@ func (s *RepoTestSuite) TestCreateRepositoryInvalidDBCreateErr() { s.adminCtx, s.Fixtures.CreateRepoParams.Owner, s.Fixtures.CreateRepoParams.Name, - s.Fixtures.CreateRepoParams.CredentialsName, + s.testCreds, s.Fixtures.CreateRepoParams.WebhookSecret, params.PoolBalancerTypeRoundRobin, ) diff --git a/database/sql/scalesets_test.go b/database/sql/scalesets_test.go index 9b8b241d..1313af59 100644 --- a/database/sql/scalesets_test.go +++ b/database/sql/scalesets_test.go @@ -19,7 +19,7 @@ type ScaleSetsTestSuite struct { suite.Suite Store dbCommon.Store adminCtx context.Context - creds params.GithubCredentials + creds params.ForgeCredentials org params.Organization repo params.Repository @@ -53,7 +53,7 @@ func (s *ScaleSetsTestSuite) SetupTest() { s.FailNow(fmt.Sprintf("failed to create org: %s", err)) } - s.repo, err = s.Store.CreateRepository(s.adminCtx, "test-org", "test-repo", s.creds.Name, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) + s.repo, err = s.Store.CreateRepository(s.adminCtx, "test-org", "test-repo", s.creds, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) if err != nil { s.FailNow(fmt.Sprintf("failed to create repo: %s", err)) } diff --git a/database/sql/sql.go b/database/sql/sql.go index 82601316..167e90ed 100644 --- a/database/sql/sql.go +++ b/database/sql/sql.go @@ -435,6 +435,7 @@ func (s *sqlDatabase) migrateDB() error { &User{}, &GithubEndpoint{}, &GithubCredentials{}, + &GiteaCredentials{}, &Tag{}, &Pool{}, &Repository{}, diff --git a/database/sql/util.go b/database/sql/util.go index a2531449..0c71261d 100644 --- a/database/sql/util.go +++ b/database/sql/util.go @@ -27,6 +27,7 @@ import ( runnerErrors "github.com/cloudbase/garm-provider-common/errors" commonParams "github.com/cloudbase/garm-provider-common/params" "github.com/cloudbase/garm-provider-common/util" + "github.com/cloudbase/garm/auth" dbCommon "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/params" ) @@ -155,7 +156,7 @@ func (s *sqlDatabase) sqlToCommonOrganization(org Organization, detailed bool) ( } if detailed { - creds, err := s.sqlToCommonGithubCredentials(org.Credentials) + creds, err := s.sqlToCommonForgeCredentials(org.Credentials) if err != nil { return params.Organization{}, errors.Wrap(err, "converting credentials") } @@ -206,7 +207,7 @@ func (s *sqlDatabase) sqlToCommonEnterprise(enterprise Enterprise, detailed bool } if detailed { - creds, err := s.sqlToCommonGithubCredentials(enterprise.Credentials) + creds, err := s.sqlToCommonForgeCredentials(enterprise.Credentials) if err != nil { return params.Enterprise{}, errors.Wrap(err, "converting credentials") } @@ -371,16 +372,28 @@ func (s *sqlDatabase) sqlToCommonRepository(repo Repository, detailed bool) (par Endpoint: endpoint, } + if repo.CredentialsID != nil && repo.GiteaCredentialsID != nil { + return params.Repository{}, runnerErrors.NewConflictError("both gitea and github credentials are set for repo %s", repo.Name) + } + + var forgeCreds params.ForgeCredentials if repo.CredentialsID != nil { ret.CredentialsID = *repo.CredentialsID + forgeCreds, err = s.sqlToCommonForgeCredentials(repo.Credentials) + } + + if repo.GiteaCredentialsID != nil { + ret.CredentialsID = *repo.GiteaCredentialsID + forgeCreds, err = s.sqlGiteaToCommonForgeCredentials(repo.GiteaCredentials) + } + + if err != nil { + return params.Repository{}, errors.Wrap(err, "converting credentials") } if detailed { - creds, err := s.sqlToCommonGithubCredentials(repo.Credentials) - if err != nil { - return params.Repository{}, errors.Wrap(err, "converting credentials") - } - ret.Credentials = creds + ret.Credentials = forgeCreds + ret.CredentialsName = forgeCreds.Name } if ret.PoolBalancerType == "" { @@ -638,7 +651,7 @@ func (s *sqlDatabase) addRepositoryEvent(ctx context.Context, repoID string, eve return errors.Wrap(err, "updating instance") } - msg := InstanceStatusUpdate{ + msg := RepositoryEvent{ Message: statusMessage, EventType: event, EventLevel: eventLevel, @@ -653,8 +666,8 @@ func (s *sqlDatabase) addRepositoryEvent(ctx context.Context, repoID string, eve if err != nil { return errors.Wrap(runnerErrors.ErrBadRequest, "parsing id") } - var latestEvents []OrganizationEvent - q := s.conn.Model(&OrganizationEvent{}). + var latestEvents []RepositoryEvent + q := s.conn.Model(&RepositoryEvent{}). Limit(maxEvents).Order("id desc"). Where("repo_id = ?", repoID).Find(&latestEvents) if q.Error != nil { @@ -662,7 +675,7 @@ func (s *sqlDatabase) addRepositoryEvent(ctx context.Context, repoID string, eve } if len(latestEvents) == maxEvents { lastInList := latestEvents[len(latestEvents)-1] - if err := s.conn.Where("repo_id = ? and id < ?", repoID, lastInList.ID).Unscoped().Delete(&OrganizationEvent{}).Error; err != nil { + if err := s.conn.Where("repo_id = ? and id < ?", repoID, lastInList.ID).Unscoped().Delete(&RepositoryEvent{}).Error; err != nil { return errors.Wrap(err, "deleting old events") } } @@ -676,7 +689,7 @@ func (s *sqlDatabase) addOrgEvent(ctx context.Context, orgID string, event param return errors.Wrap(err, "updating instance") } - msg := InstanceStatusUpdate{ + msg := OrganizationEvent{ Message: statusMessage, EventType: event, EventLevel: eventLevel, @@ -714,7 +727,7 @@ func (s *sqlDatabase) addEnterpriseEvent(ctx context.Context, entID string, even return errors.Wrap(err, "updating instance") } - msg := InstanceStatusUpdate{ + msg := EnterpriseEvent{ Message: statusMessage, EventType: event, EventLevel: eventLevel, @@ -763,3 +776,135 @@ func (s *sqlDatabase) AddEntityEvent(ctx context.Context, entity params.ForgeEnt return errors.Wrap(runnerErrors.ErrBadRequest, "invalid entity type") } } + +func (s *sqlDatabase) sqlToCommonForgeCredentials(creds GithubCredentials) (params.ForgeCredentials, error) { + if len(creds.Payload) == 0 { + return params.ForgeCredentials{}, errors.New("empty credentials payload") + } + data, err := util.Unseal(creds.Payload, []byte(s.cfg.Passphrase)) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "unsealing credentials") + } + + ep, err := s.sqlToCommonGithubEndpoint(creds.Endpoint) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "converting github endpoint") + } + + commonCreds := params.ForgeCredentials{ + ID: creds.ID, + Name: creds.Name, + Description: creds.Description, + APIBaseURL: creds.Endpoint.APIBaseURL, + BaseURL: creds.Endpoint.BaseURL, + UploadBaseURL: creds.Endpoint.UploadBaseURL, + CABundle: creds.Endpoint.CACertBundle, + AuthType: creds.AuthType, + CreatedAt: creds.CreatedAt, + UpdatedAt: creds.UpdatedAt, + ForgeType: creds.Endpoint.EndpointType, + Endpoint: ep, + CredentialsPayload: data, + } + + for _, repo := range creds.Repositories { + commonRepo, err := s.sqlToCommonRepository(repo, false) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "converting github repository") + } + commonCreds.Repositories = append(commonCreds.Repositories, commonRepo) + } + + for _, org := range creds.Organizations { + commonOrg, err := s.sqlToCommonOrganization(org, false) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "converting github organization") + } + commonCreds.Organizations = append(commonCreds.Organizations, commonOrg) + } + + for _, ent := range creds.Enterprises { + commonEnt, err := s.sqlToCommonEnterprise(ent, false) + if err != nil { + return params.ForgeCredentials{}, errors.Wrapf(err, "converting github enterprise: %s", ent.Name) + } + commonCreds.Enterprises = append(commonCreds.Enterprises, commonEnt) + } + + return commonCreds, nil +} + +func (s *sqlDatabase) sqlGiteaToCommonForgeCredentials(creds GiteaCredentials) (params.ForgeCredentials, error) { + if len(creds.Payload) == 0 { + return params.ForgeCredentials{}, errors.New("empty credentials payload") + } + data, err := util.Unseal(creds.Payload, []byte(s.cfg.Passphrase)) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "unsealing credentials") + } + + ep, err := s.sqlToCommonGithubEndpoint(creds.Endpoint) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "converting github endpoint") + } + + commonCreds := params.ForgeCredentials{ + ID: creds.ID, + Name: creds.Name, + Description: creds.Description, + APIBaseURL: creds.Endpoint.APIBaseURL, + BaseURL: creds.Endpoint.BaseURL, + CABundle: creds.Endpoint.CACertBundle, + AuthType: creds.AuthType, + CreatedAt: creds.CreatedAt, + UpdatedAt: creds.UpdatedAt, + ForgeType: creds.Endpoint.EndpointType, + Endpoint: ep, + CredentialsPayload: data, + } + + for _, repo := range creds.Repositories { + commonRepo, err := s.sqlToCommonRepository(repo, false) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "converting github repository") + } + commonCreds.Repositories = append(commonCreds.Repositories, commonRepo) + } + + for _, org := range creds.Organizations { + commonOrg, err := s.sqlToCommonOrganization(org, false) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "converting github organization") + } + commonCreds.Organizations = append(commonCreds.Organizations, commonOrg) + } + + return commonCreds, nil +} + +func (s *sqlDatabase) sqlToCommonGithubEndpoint(ep GithubEndpoint) (params.ForgeEndpoint, error) { + return params.ForgeEndpoint{ + Name: ep.Name, + Description: ep.Description, + APIBaseURL: ep.APIBaseURL, + BaseURL: ep.BaseURL, + UploadBaseURL: ep.UploadBaseURL, + CACertBundle: ep.CACertBundle, + CreatedAt: ep.CreatedAt, + EndpointType: ep.EndpointType, + UpdatedAt: ep.UpdatedAt, + }, nil +} + +func getUIDFromContext(ctx context.Context) (uuid.UUID, error) { + userID := auth.UserID(ctx) + if userID == "" { + return uuid.Nil, errors.Wrap(runnerErrors.ErrUnauthorized, "getting UID from context") + } + + asUUID, err := uuid.Parse(userID) + if err != nil { + return uuid.Nil, errors.Wrap(runnerErrors.ErrUnauthorized, "parsing UID from context") + } + return asUUID, nil +} diff --git a/database/watcher/filters.go b/database/watcher/filters.go index 51820270..c355890b 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -189,7 +189,7 @@ func WithForgeCredentialsFilter(creds params.ForgeCredentials) dbCommon.PayloadF var ok bool switch payload.EntityType { case dbCommon.GithubCredentialsEntityType: - idGetter, ok = payload.Payload.(params.GithubCredentials) + idGetter, ok = payload.Payload.(params.ForgeCredentials) default: return false } diff --git a/database/watcher/watcher_store_test.go b/database/watcher/watcher_store_test.go index 2300ac0a..8791a514 100644 --- a/database/watcher/watcher_store_test.go +++ b/database/watcher/watcher_store_test.go @@ -155,7 +155,7 @@ func (s *WatcherStoreTestSuite) TestInstanceWatcher() { creds := garmTesting.CreateTestGithubCredentials(s.ctx, "test-creds", s.store, s.T(), ep) s.T().Cleanup(func() { s.store.DeleteGithubCredentials(s.ctx, creds.ID) }) - repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds.Name, "test-secret", params.PoolBalancerTypeRoundRobin) + repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds, "test-secret", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotEmpty(repo.ID) s.T().Cleanup(func() { s.store.DeleteRepository(s.ctx, repo.ID) }) @@ -259,7 +259,7 @@ func (s *WatcherStoreTestSuite) TestScaleSetInstanceWatcher() { creds := garmTesting.CreateTestGithubCredentials(s.ctx, "test-creds", s.store, s.T(), ep) s.T().Cleanup(func() { s.store.DeleteGithubCredentials(s.ctx, creds.ID) }) - repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds.Name, "test-secret", params.PoolBalancerTypeRoundRobin) + repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds, "test-secret", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotEmpty(repo.ID) s.T().Cleanup(func() { s.store.DeleteRepository(s.ctx, repo.ID) }) @@ -369,7 +369,7 @@ func (s *WatcherStoreTestSuite) TestPoolWatcher() { } }) - repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds.Name, "test-secret", params.PoolBalancerTypeRoundRobin) + repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds, "test-secret", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotEmpty(repo.ID) s.T().Cleanup(func() { s.store.DeleteRepository(s.ctx, repo.ID) }) @@ -490,7 +490,7 @@ func (s *WatcherStoreTestSuite) TestScaleSetWatcher() { } }) - repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds.Name, "test-secret", params.PoolBalancerTypeRoundRobin) + repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds, "test-secret", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotEmpty(repo.ID) s.T().Cleanup(func() { s.store.DeleteRepository(s.ctx, repo.ID) }) @@ -780,7 +780,7 @@ func (s *WatcherStoreTestSuite) TestRepoWatcher() { creds := garmTesting.CreateTestGithubCredentials(s.ctx, "test-creds", s.store, s.T(), ep) s.T().Cleanup(func() { s.store.DeleteGithubCredentials(s.ctx, creds.ID) }) - repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds.Name, "test-secret", params.PoolBalancerTypeRoundRobin) + repo, err := s.store.CreateRepository(s.ctx, "test-owner", "test-repo", creds, "test-secret", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotEmpty(repo.ID) @@ -898,7 +898,7 @@ func (s *WatcherStoreTestSuite) TestGithubCredentialsWatcher() { EntityType: common.GithubCredentialsEntityType, Operation: common.DeleteOperation, // We only get the ID and Name of the deleted entity - Payload: params.GithubCredentials{ID: ghCred.ID, Name: ghCred.Name}, + Payload: params.ForgeCredentials{ID: ghCred.ID, Name: ghCred.Name}, }, event) case <-time.After(1 * time.Second): s.T().Fatal("expected payload not received") diff --git a/go.mod b/go.mod index a0b3901f..5070dbfe 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 golang.org/x/crypto v0.38.0 + golang.org/x/mod v0.17.0 golang.org/x/oauth2 v0.30.0 golang.org/x/sync v0.14.0 gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 diff --git a/go.sum b/go.sum index 3c9af9bb..1cbc5ee0 100644 --- a/go.sum +++ b/go.sum @@ -190,6 +190,8 @@ go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= diff --git a/internal/testing/testing.go b/internal/testing/testing.go index 6f253267..0fcc1dda 100644 --- a/internal/testing/testing.go +++ b/internal/testing/testing.go @@ -110,7 +110,7 @@ func CreateDefaultGithubEndpoint(ctx context.Context, db common.Store, s *testin return ep } -func CreateTestGithubCredentials(ctx context.Context, credsName string, db common.Store, s *testing.T, endpoint params.ForgeEndpoint) params.GithubCredentials { +func CreateTestGithubCredentials(ctx context.Context, credsName string, db common.Store, s *testing.T, endpoint params.ForgeEndpoint) params.ForgeCredentials { newCredsParams := params.CreateGithubCredentialsParams{ Name: credsName, Description: "Test creds", diff --git a/params/params.go b/params/params.go index daa54b61..73afa0f4 100644 --- a/params/params.go +++ b/params/params.go @@ -344,26 +344,30 @@ type Tag struct { type Pool struct { RunnerPrefix - ID string `json:"id,omitempty"` - ProviderName string `json:"provider_name,omitempty"` - MaxRunners uint `json:"max_runners,omitempty"` - MinIdleRunners uint `json:"min_idle_runners,omitempty"` - Image string `json:"image,omitempty"` - Flavor string `json:"flavor,omitempty"` - OSType commonParams.OSType `json:"os_type,omitempty"` - OSArch commonParams.OSArch `json:"os_arch,omitempty"` - Tags []Tag `json:"tags,omitempty"` - Enabled bool `json:"enabled,omitempty"` - Instances []Instance `json:"instances,omitempty"` - 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,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty"` + ID string `json:"id,omitempty"` + ProviderName string `json:"provider_name,omitempty"` + MaxRunners uint `json:"max_runners,omitempty"` + MinIdleRunners uint `json:"min_idle_runners,omitempty"` + Image string `json:"image,omitempty"` + Flavor string `json:"flavor,omitempty"` + OSType commonParams.OSType `json:"os_type,omitempty"` + OSArch commonParams.OSArch `json:"os_arch,omitempty"` + Tags []Tag `json:"tags,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Instances []Instance `json:"instances,omitempty"` + + 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,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` // 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 @@ -586,9 +590,11 @@ type Repository struct { // CredentialName is the name of the credentials associated with the enterprise. // This field is now deprecated. Use CredentialsID instead. This field will be // removed in v0.2.0. - CredentialsName string `json:"credentials_name,omitempty"` - CredentialsID uint `json:"credentials_id,omitempty"` - Credentials GithubCredentials `json:"credentials,omitempty"` + CredentialsName string `json:"credentials_name,omitempty"` + + CredentialsID uint `json:"credentials_id,omitempty"` + Credentials ForgeCredentials `json:"credentials,omitempty"` + PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancing_type,omitempty"` Endpoint ForgeEndpoint `json:"endpoint,omitempty"` @@ -598,6 +604,13 @@ type Repository struct { WebhookSecret string `json:"-"` } +func (r Repository) GetCredentialsName() string { + if r.CredentialsName != "" { + return r.CredentialsName + } + return r.Credentials.Name +} + func (r Repository) CreationDateGetter() time.Time { return r.CreatedAt } @@ -612,13 +625,10 @@ func (r Repository) GetEntity() (ForgeEntity, error) { Owner: r.Owner, Name: r.Name, PoolBalancerType: r.PoolBalancerType, - Credentials: ForgeCredentials{ - ForgeType: GithubEndpointType, - GithubCredentials: r.Credentials, - }, - WebhookSecret: r.WebhookSecret, - CreatedAt: r.CreatedAt, - UpdatedAt: r.UpdatedAt, + Credentials: r.Credentials, + WebhookSecret: r.WebhookSecret, + CreatedAt: r.CreatedAt, + UpdatedAt: r.UpdatedAt, }, nil } @@ -652,7 +662,7 @@ type Organization struct { // This field is now deprecated. Use CredentialsID instead. This field will be // removed in v0.2.0. CredentialsName string `json:"credentials_name,omitempty"` - Credentials GithubCredentials `json:"credentials,omitempty"` + Credentials ForgeCredentials `json:"credentials,omitempty"` CredentialsID uint `json:"credentials_id,omitempty"` PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancing_type,omitempty"` @@ -677,12 +687,9 @@ func (o Organization) GetEntity() (ForgeEntity, error) { Owner: o.Name, WebhookSecret: o.WebhookSecret, PoolBalancerType: o.PoolBalancerType, - Credentials: ForgeCredentials{ - ForgeType: GithubEndpointType, - GithubCredentials: o.Credentials, - }, - CreatedAt: o.CreatedAt, - UpdatedAt: o.UpdatedAt, + Credentials: o.Credentials, + CreatedAt: o.CreatedAt, + UpdatedAt: o.UpdatedAt, }, nil } @@ -712,7 +719,7 @@ type Enterprise struct { // This field is now deprecated. Use CredentialsID instead. This field will be // removed in v0.2.0. CredentialsName string `json:"credentials_name,omitempty"` - Credentials GithubCredentials `json:"credentials,omitempty"` + Credentials ForgeCredentials `json:"credentials,omitempty"` CredentialsID uint `json:"credentials_id,omitempty"` PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancing_type,omitempty"` @@ -737,12 +744,9 @@ func (e Enterprise) GetEntity() (ForgeEntity, error) { Owner: e.Name, WebhookSecret: e.WebhookSecret, PoolBalancerType: e.PoolBalancerType, - Credentials: ForgeCredentials{ - ForgeType: GithubEndpointType, - GithubCredentials: e.Credentials, - }, - CreatedAt: e.CreatedAt, - UpdatedAt: e.UpdatedAt, + Credentials: e.Credentials, + CreatedAt: e.CreatedAt, + UpdatedAt: e.UpdatedAt, }, nil } @@ -856,99 +860,6 @@ func (g GithubRateLimit) ResetAt() time.Time { } type ForgeCredentials struct { - ForgeType EndpointType `json:"type,omitempty"` - GithubCredentials GithubCredentials `json:"github,omitempty"` -} - -func (f ForgeCredentials) CABundle() []byte { - switch f.ForgeType { - case GithubEndpointType: - return f.GithubCredentials.CABundle - case GiteaEndpointType: - return nil - default: - return nil - } -} - -func (f ForgeCredentials) Endpoint() ForgeEndpoint { - switch f.ForgeType { - case GithubEndpointType: - return f.GithubCredentials.Endpoint - case GiteaEndpointType: - return ForgeEndpoint{} - default: - return ForgeEndpoint{} - } -} - -func (f ForgeCredentials) APIBaseURL() string { - switch f.ForgeType { - case GithubEndpointType: - return f.GithubCredentials.APIBaseURL - case GiteaEndpointType: - return "" - default: - return "" - } -} - -func (f ForgeCredentials) UploadBaseURL() string { - switch f.ForgeType { - case GithubEndpointType: - return f.GithubCredentials.UploadBaseURL - case GiteaEndpointType: - return "" - default: - return "" - } -} - -func (f ForgeCredentials) BaseURL() string { - switch f.ForgeType { - case GithubEndpointType: - return f.GithubCredentials.BaseURL - case GiteaEndpointType: - return "" - default: - return "" - } -} - -func (f ForgeCredentials) GetHTTPClient(ctx context.Context) (*http.Client, error) { - switch f.ForgeType { - case GithubEndpointType: - return f.GithubCredentials.GetHTTPClient(ctx) - case GiteaEndpointType: - return nil, fmt.Errorf("gitea credentials not supported") - default: - return nil, fmt.Errorf("unknown credentials type") - } -} - -func (f ForgeCredentials) GetID() uint { - switch f.ForgeType { - case GithubEndpointType: - return f.GithubCredentials.ID - case GiteaEndpointType: - return 0 - default: - return 0 - } -} - -func (f ForgeCredentials) RootCertificateBundle() (CertificateBundle, error) { - switch f.ForgeType { - case GithubEndpointType: - return f.GithubCredentials.RootCertificateBundle() - case GiteaEndpointType: - return CertificateBundle{}, fmt.Errorf("gitea credentials not supported") - default: - return CertificateBundle{}, fmt.Errorf("unknown credentials type") - } -} - -type GithubCredentials struct { ID uint `json:"id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` @@ -958,30 +869,25 @@ type GithubCredentials struct { CABundle []byte `json:"ca_bundle,omitempty"` AuthType ForgeAuthType `json:"auth-type,omitempty"` - Repositories []Repository `json:"repositories,omitempty"` - Organizations []Organization `json:"organizations,omitempty"` - Enterprises []Enterprise `json:"enterprises,omitempty"` - Endpoint ForgeEndpoint `json:"endpoint,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty"` - RateLimit GithubRateLimit `json:"rate_limit,omitempty"` + ForgeType EndpointType `json:"forge_type,omitempty"` + + Repositories []Repository `json:"repositories,omitempty"` + Organizations []Organization `json:"organizations,omitempty"` + Enterprises []Enterprise `json:"enterprises,omitempty"` + Endpoint ForgeEndpoint `json:"endpoint,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + RateLimit *GithubRateLimit `json:"rate_limit,omitempty"` // Do not serialize sensitive info. CredentialsPayload []byte `json:"-"` } -func (g GithubCredentials) GetID() uint { +func (g ForgeCredentials) GetID() uint { return g.ID } -func (g GithubCredentials) GetForgeCredentials() ForgeCredentials { - return ForgeCredentials{ - ForgeType: GithubEndpointType, - GithubCredentials: g, - } -} - -func (g GithubCredentials) GetHTTPClient(ctx context.Context) (*http.Client, error) { +func (g ForgeCredentials) GetHTTPClient(ctx context.Context) (*http.Client, error) { var roots *x509.CertPool if g.CABundle != nil { roots = x509.NewCertPool() @@ -1036,7 +942,7 @@ func (g GithubCredentials) GetHTTPClient(ctx context.Context) (*http.Client, err return tc, nil } -func (g GithubCredentials) RootCertificateBundle() (CertificateBundle, error) { +func (g ForgeCredentials) RootCertificateBundle() (CertificateBundle, error) { if len(g.CABundle) == 0 { return CertificateBundle{}, nil } @@ -1067,7 +973,7 @@ func (g GithubCredentials) RootCertificateBundle() (CertificateBundle, error) { } // used by swagger client generated code -type Credentials []GithubCredentials +type Credentials []ForgeCredentials type Provider struct { Name string `json:"name,omitempty"` @@ -1195,11 +1101,11 @@ func (g ForgeEntity) GetCreatedAt() time.Time { func (g ForgeEntity) ForgeURL() string { switch g.EntityType { case ForgeEntityTypeRepository: - return fmt.Sprintf("%s/%s/%s", g.Credentials.BaseURL(), g.Owner, g.Name) + return fmt.Sprintf("%s/%s/%s", g.Credentials.BaseURL, g.Owner, g.Name) case ForgeEntityTypeOrganization: - return fmt.Sprintf("%s/%s", g.Credentials.BaseURL(), g.Owner) + return fmt.Sprintf("%s/%s", g.Credentials.BaseURL, g.Owner) case ForgeEntityTypeEnterprise: - return fmt.Sprintf("%s/enterprises/%s", g.Credentials.BaseURL(), g.Owner) + return fmt.Sprintf("%s/enterprises/%s", g.Credentials.BaseURL, g.Owner) } return "" } diff --git a/params/requests.go b/params/requests.go index 7ab1fa91..82cbf113 100644 --- a/params/requests.go +++ b/params/requests.go @@ -45,6 +45,16 @@ type CreateRepoParams struct { CredentialsName string `json:"credentials_name,omitempty"` WebhookSecret string `json:"webhook_secret,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancer_type,omitempty"` + ForgeType EndpointType `json:"forge_type,omitempty"` +} + +func (c CreateRepoParams) GetForgeType() EndpointType { + switch c.ForgeType { + case GithubEndpointType, GiteaEndpointType: + return c.ForgeType + default: + return GithubEndpointType + } } func (c *CreateRepoParams) Validate() error { @@ -77,6 +87,16 @@ type CreateOrgParams struct { CredentialsName string `json:"credentials_name,omitempty"` WebhookSecret string `json:"webhook_secret,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancer_type,omitempty"` + ForgeType EndpointType `json:"forge_type,omitempty"` +} + +func (c CreateOrgParams) GetForgeType() EndpointType { + switch c.ForgeType { + case GithubEndpointType, GiteaEndpointType: + return c.ForgeType + default: + return GithubEndpointType + } } func (c *CreateOrgParams) Validate() error { @@ -281,7 +301,6 @@ type CreateGithubEndpointParams struct { APIBaseURL string `json:"api_base_url,omitempty"` UploadBaseURL string `json:"upload_base_url,omitempty"` BaseURL string `json:"base_url,omitempty"` - EndpointType string `json:"endpoint_type,omitempty"` CACertBundle []byte `json:"ca_cert_bundle,omitempty"` } @@ -290,14 +309,6 @@ func (c CreateGithubEndpointParams) Validate() error { return runnerErrors.NewBadRequestError("missing api_base_url") } - if c.EndpointType != "" { - switch c.EndpointType { - case string(GithubEndpointType), string(GiteaEndpointType): - default: - return runnerErrors.NewBadRequestError("invalid endpoint_type: %s", c.EndpointType) - } - } - url, err := url.Parse(c.APIBaseURL) if err != nil || url.Scheme == "" || url.Host == "" { return runnerErrors.NewBadRequestError("invalid api_base_url") @@ -308,21 +319,19 @@ func (c CreateGithubEndpointParams) Validate() error { return runnerErrors.NewBadRequestError("invalid api_base_url") } - if c.EndpointType == string(GithubEndpointType) { - if c.UploadBaseURL == "" { - return runnerErrors.NewBadRequestError("missing upload_base_url") - } + if c.UploadBaseURL == "" { + return runnerErrors.NewBadRequestError("missing upload_base_url") + } - url, err = url.Parse(c.UploadBaseURL) - if err != nil || url.Scheme == "" || url.Host == "" { - return runnerErrors.NewBadRequestError("invalid upload_base_url") - } + url, err = url.Parse(c.UploadBaseURL) + if err != nil || url.Scheme == "" || url.Host == "" { + return runnerErrors.NewBadRequestError("invalid upload_base_url") + } - switch url.Scheme { - case httpsScheme, httpScheme: - default: - return runnerErrors.NewBadRequestError("invalid api_base_url") - } + switch url.Scheme { + case httpsScheme, httpScheme: + default: + return runnerErrors.NewBadRequestError("invalid api_base_url") } if c.BaseURL == "" { @@ -617,3 +626,154 @@ type UpdateScaleSetParams struct { State *ScaleSetState `json:"state"` ExtendedState *string `json:"extended_state"` } + +type CreateGiteaEndpointParams struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + APIBaseURL string `json:"api_base_url,omitempty"` + BaseURL string `json:"base_url,omitempty"` + CACertBundle []byte `json:"ca_cert_bundle,omitempty"` +} + +func (c CreateGiteaEndpointParams) Validate() error { + if c.APIBaseURL == "" { + return runnerErrors.NewBadRequestError("missing api_base_url") + } + + url, err := url.Parse(c.APIBaseURL) + if err != nil || url.Scheme == "" || url.Host == "" { + return runnerErrors.NewBadRequestError("invalid api_base_url") + } + switch url.Scheme { + case httpsScheme, httpScheme: + default: + return runnerErrors.NewBadRequestError("invalid api_base_url") + } + + switch url.Scheme { + case httpsScheme, httpScheme: + default: + return runnerErrors.NewBadRequestError("invalid api_base_url") + } + + if c.BaseURL == "" { + return runnerErrors.NewBadRequestError("missing base_url") + } + + url, err = url.Parse(c.BaseURL) + if err != nil || url.Scheme == "" || url.Host == "" { + return runnerErrors.NewBadRequestError("invalid base_url") + } + + switch url.Scheme { + case httpsScheme, httpScheme: + default: + return runnerErrors.NewBadRequestError("invalid api_base_url") + } + + if c.CACertBundle != nil { + block, _ := pem.Decode(c.CACertBundle) + if block == nil { + return runnerErrors.NewBadRequestError("invalid ca_cert_bundle") + } + if _, err := x509.ParseCertificates(block.Bytes); err != nil { + return runnerErrors.NewBadRequestError("invalid ca_cert_bundle") + } + } + + return nil +} + +type UpdateGiteaEndpointParams struct { + Description *string `json:"description,omitempty"` + APIBaseURL *string `json:"api_base_url,omitempty"` + BaseURL *string `json:"base_url,omitempty"` + CACertBundle []byte `json:"ca_cert_bundle,omitempty"` +} + +func (u UpdateGiteaEndpointParams) Validate() error { + if u.APIBaseURL != nil { + url, err := url.Parse(*u.APIBaseURL) + if err != nil || url.Scheme == "" || url.Host == "" { + return runnerErrors.NewBadRequestError("invalid api_base_url") + } + switch url.Scheme { + case httpsScheme, httpScheme: + default: + return runnerErrors.NewBadRequestError("invalid api_base_url") + } + } + + if u.BaseURL != nil { + url, err := url.Parse(*u.BaseURL) + if err != nil || url.Scheme == "" || url.Host == "" { + return runnerErrors.NewBadRequestError("invalid base_url") + } + switch url.Scheme { + case httpsScheme, httpScheme: + default: + return runnerErrors.NewBadRequestError("invalid api_base_url") + } + } + + if u.CACertBundle != nil { + block, _ := pem.Decode(u.CACertBundle) + if block == nil { + return runnerErrors.NewBadRequestError("invalid ca_cert_bundle") + } + if _, err := x509.ParseCertificates(block.Bytes); err != nil { + return runnerErrors.NewBadRequestError("invalid ca_cert_bundle") + } + } + + return nil +} + +type CreateGiteaCredentialsParams struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Endpoint string `json:"endpoint,omitempty"` + AuthType ForgeAuthType `json:"auth_type,omitempty"` + PAT GithubPAT `json:"pat,omitempty"` + App GithubApp `json:"app,omitempty"` +} + +func (c CreateGiteaCredentialsParams) Validate() error { + if c.Name == "" { + return runnerErrors.NewBadRequestError("missing name") + } + + if c.Endpoint == "" { + return runnerErrors.NewBadRequestError("missing endpoint") + } + + switch c.AuthType { + case ForgeAuthTypePAT: + default: + return runnerErrors.NewBadRequestError("invalid auth_type: %s", c.AuthType) + } + + if c.AuthType == ForgeAuthTypePAT { + if c.PAT.OAuth2Token == "" { + return runnerErrors.NewBadRequestError("missing oauth2_token") + } + } + + return nil +} + +type UpdateGiteaCredentialsParams struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + PAT *GithubPAT `json:"pat,omitempty"` +} + +func (u UpdateGiteaCredentialsParams) Validate() error { + if u.PAT != nil { + if u.PAT.OAuth2Token == "" { + return runnerErrors.NewBadRequestError("missing oauth2_token") + } + } + + return nil +} diff --git a/runner/common/mocks/GithubClient.go b/runner/common/mocks/GithubClient.go index 6ba39d48..36ef1079 100644 --- a/runner/common/mocks/GithubClient.go +++ b/runner/common/mocks/GithubClient.go @@ -342,7 +342,7 @@ func (_m *GithubClient) ListEntityRunnerApplicationDownloads(ctx context.Context } // 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) { +func (_m *GithubClient) ListEntityRunners(ctx context.Context, opts *github.ListRunnersOptions) (*github.Runners, *github.Response, error) { ret := _m.Called(ctx, opts) if len(ret) == 0 { @@ -352,10 +352,10 @@ func (_m *GithubClient) ListEntityRunners(ctx context.Context, opts *github.List 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 { + if rf, ok := ret.Get(0).(func(context.Context, *github.ListRunnersOptions) (*github.Runners, *github.Response, error)); ok { return rf(ctx, opts) } - if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) *github.Runners); ok { + if rf, ok := ret.Get(0).(func(context.Context, *github.ListRunnersOptions) *github.Runners); ok { r0 = rf(ctx, opts) } else { if ret.Get(0) != nil { @@ -363,7 +363,7 @@ func (_m *GithubClient) ListEntityRunners(ctx context.Context, opts *github.List } } - if rf, ok := ret.Get(1).(func(context.Context, *github.ListOptions) *github.Response); ok { + if rf, ok := ret.Get(1).(func(context.Context, *github.ListRunnersOptions) *github.Response); ok { r1 = rf(ctx, opts) } else { if ret.Get(1) != nil { @@ -371,7 +371,7 @@ func (_m *GithubClient) ListEntityRunners(ctx context.Context, opts *github.List } } - if rf, ok := ret.Get(2).(func(context.Context, *github.ListOptions) error); ok { + if rf, ok := ret.Get(2).(func(context.Context, *github.ListRunnersOptions) error); ok { r2 = rf(ctx, opts) } else { r2 = ret.Error(2) @@ -410,6 +410,36 @@ func (_m *GithubClient) PingEntityHook(ctx context.Context, id int64) (*github.R return r0, r1 } +// RateLimit provides a mock function with given fields: ctx +func (_m *GithubClient) RateLimit(ctx context.Context) (*github.RateLimits, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for RateLimit") + } + + var r0 *github.RateLimits + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*github.RateLimits, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *github.RateLimits); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.RateLimits) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // RemoveEntityRunner provides a mock function with given fields: ctx, runnerID func (_m *GithubClient) RemoveEntityRunner(ctx context.Context, runnerID int64) error { ret := _m.Called(ctx, runnerID) diff --git a/runner/common/mocks/GithubEntityOperations.go b/runner/common/mocks/GithubEntityOperations.go index 567d4ebc..0aab9943 100644 --- a/runner/common/mocks/GithubEntityOperations.go +++ b/runner/common/mocks/GithubEntityOperations.go @@ -303,7 +303,7 @@ func (_m *GithubEntityOperations) ListEntityRunnerApplicationDownloads(ctx conte } // 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) { +func (_m *GithubEntityOperations) ListEntityRunners(ctx context.Context, opts *github.ListRunnersOptions) (*github.Runners, *github.Response, error) { ret := _m.Called(ctx, opts) if len(ret) == 0 { @@ -313,10 +313,10 @@ func (_m *GithubEntityOperations) ListEntityRunners(ctx context.Context, opts *g 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 { + if rf, ok := ret.Get(0).(func(context.Context, *github.ListRunnersOptions) (*github.Runners, *github.Response, error)); ok { return rf(ctx, opts) } - if rf, ok := ret.Get(0).(func(context.Context, *github.ListOptions) *github.Runners); ok { + if rf, ok := ret.Get(0).(func(context.Context, *github.ListRunnersOptions) *github.Runners); ok { r0 = rf(ctx, opts) } else { if ret.Get(0) != nil { @@ -324,7 +324,7 @@ func (_m *GithubEntityOperations) ListEntityRunners(ctx context.Context, opts *g } } - if rf, ok := ret.Get(1).(func(context.Context, *github.ListOptions) *github.Response); ok { + if rf, ok := ret.Get(1).(func(context.Context, *github.ListRunnersOptions) *github.Response); ok { r1 = rf(ctx, opts) } else { if ret.Get(1) != nil { @@ -332,7 +332,7 @@ func (_m *GithubEntityOperations) ListEntityRunners(ctx context.Context, opts *g } } - if rf, ok := ret.Get(2).(func(context.Context, *github.ListOptions) error); ok { + if rf, ok := ret.Get(2).(func(context.Context, *github.ListRunnersOptions) error); ok { r2 = rf(ctx, opts) } else { r2 = ret.Error(2) @@ -371,6 +371,36 @@ func (_m *GithubEntityOperations) PingEntityHook(ctx context.Context, id int64) return r0, r1 } +// RateLimit provides a mock function with given fields: ctx +func (_m *GithubEntityOperations) RateLimit(ctx context.Context) (*github.RateLimits, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for RateLimit") + } + + var r0 *github.RateLimits + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*github.RateLimits, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *github.RateLimits); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.RateLimits) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } 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) error { ret := _m.Called(ctx, runnerID) diff --git a/runner/common/mocks/RateLimitClient.go b/runner/common/mocks/RateLimitClient.go new file mode 100644 index 00000000..2c360217 --- /dev/null +++ b/runner/common/mocks/RateLimitClient.go @@ -0,0 +1,59 @@ +// Code generated by mockery v2.53.3. DO NOT EDIT. + +package mocks + +import ( + context "context" + + github "github.com/google/go-github/v71/github" + mock "github.com/stretchr/testify/mock" +) + +// RateLimitClient is an autogenerated mock type for the RateLimitClient type +type RateLimitClient struct { + mock.Mock +} + +// RateLimit provides a mock function with given fields: ctx +func (_m *RateLimitClient) RateLimit(ctx context.Context) (*github.RateLimits, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for RateLimit") + } + + var r0 *github.RateLimits + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*github.RateLimits, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *github.RateLimits); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.RateLimits) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewRateLimitClient creates a new instance of RateLimitClient. 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 NewRateLimitClient(t interface { + mock.TestingT + Cleanup(func()) +}) *RateLimitClient { + mock := &RateLimitClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/runner/enterprises_test.go b/runner/enterprises_test.go index 7e4545d4..5c09ae5d 100644 --- a/runner/enterprises_test.go +++ b/runner/enterprises_test.go @@ -39,7 +39,7 @@ type EnterpriseTestFixtures struct { Store dbCommon.Store StoreEnterprises map[string]params.Enterprise Providers map[string]common.Provider - Credentials map[string]params.GithubCredentials + Credentials map[string]params.ForgeCredentials CreateEnterpriseParams params.CreateEnterpriseParams CreatePoolParams params.CreatePoolParams CreateInstanceParams params.CreateInstanceParams @@ -56,8 +56,8 @@ type EnterpriseTestSuite struct { Fixtures *EnterpriseTestFixtures Runner *Runner - testCreds params.GithubCredentials - secondaryTestCreds params.GithubCredentials + testCreds params.ForgeCredentials + secondaryTestCreds params.ForgeCredentials forgeEndpoint params.ForgeEndpoint } @@ -103,7 +103,7 @@ func (s *EnterpriseTestSuite) SetupTest() { Providers: map[string]common.Provider{ "test-provider": providerMock, }, - Credentials: map[string]params.GithubCredentials{ + Credentials: map[string]params.ForgeCredentials{ s.testCreds.Name: s.testCreds, s.secondaryTestCreds.Name: s.secondaryTestCreds, }, diff --git a/runner/gitea_credentials.go b/runner/gitea_credentials.go new file mode 100644 index 00000000..749f2346 --- /dev/null +++ b/runner/gitea_credentials.go @@ -0,0 +1,86 @@ +package runner + +import ( + "context" + + "github.com/pkg/errors" + + runnerErrors "github.com/cloudbase/garm-provider-common/errors" + "github.com/cloudbase/garm/auth" + "github.com/cloudbase/garm/params" +) + +func (r *Runner) ListGiteaCredentials(ctx context.Context) ([]params.ForgeCredentials, error) { + if !auth.IsAdmin(ctx) { + return nil, runnerErrors.ErrUnauthorized + } + + // Get the credentials from the store. The cache is always updated after the database successfully + // commits the transaction that created/updated the credentials. + // If we create a set of credentials then immediately after we call ListGiteaCredentials, + // there is a posibillity that not all creds will be in the cache. + creds, err := r.store.ListGiteaCredentials(ctx) + if err != nil { + return nil, errors.Wrap(err, "fetching gitea credentials") + } + return creds, nil +} + +func (r *Runner) CreateGiteaCredentials(ctx context.Context, param params.CreateGiteaCredentialsParams) (params.ForgeCredentials, error) { + if !auth.IsAdmin(ctx) { + return params.ForgeCredentials{}, runnerErrors.ErrUnauthorized + } + + if err := param.Validate(); err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "failed to validate gitea credentials params") + } + + creds, err := r.store.CreateGiteaCredentials(ctx, param) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "failed to create gitea credentials") + } + + return creds, nil +} + +func (r *Runner) GetGiteaCredentials(ctx context.Context, id uint) (params.ForgeCredentials, error) { + if !auth.IsAdmin(ctx) { + return params.ForgeCredentials{}, runnerErrors.ErrUnauthorized + } + + creds, err := r.store.GetGiteaCredentials(ctx, id, true) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "failed to get gitea credentials") + } + + return creds, nil +} + +func (r *Runner) DeleteGiteaCredentials(ctx context.Context, id uint) error { + if !auth.IsAdmin(ctx) { + return runnerErrors.ErrUnauthorized + } + + if err := r.store.DeleteGiteaCredentials(ctx, id); err != nil { + return errors.Wrap(err, "failed to delete gitea credentials") + } + + return nil +} + +func (r *Runner) UpdateGiteaCredentials(ctx context.Context, id uint, param params.UpdateGiteaCredentialsParams) (params.ForgeCredentials, error) { + if !auth.IsAdmin(ctx) { + return params.ForgeCredentials{}, runnerErrors.ErrUnauthorized + } + + if err := param.Validate(); err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "failed to validate gitea credentials params") + } + + newCreds, err := r.store.UpdateGiteaCredentials(ctx, id, param) + if err != nil { + return params.ForgeCredentials{}, errors.Wrap(err, "failed to update gitea credentials") + } + + return newCreds, nil +} diff --git a/runner/gitea_endpoints.go b/runner/gitea_endpoints.go new file mode 100644 index 00000000..847dbab9 --- /dev/null +++ b/runner/gitea_endpoints.go @@ -0,0 +1,82 @@ +package runner + +import ( + "context" + + "github.com/pkg/errors" + + runnerErrors "github.com/cloudbase/garm-provider-common/errors" + "github.com/cloudbase/garm/auth" + "github.com/cloudbase/garm/params" +) + +func (r *Runner) CreateGiteaEndpoint(ctx context.Context, param params.CreateGiteaEndpointParams) (params.ForgeEndpoint, error) { + if !auth.IsAdmin(ctx) { + return params.ForgeEndpoint{}, runnerErrors.ErrUnauthorized + } + + if err := param.Validate(); err != nil { + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to validate gitea endpoint params") + } + + ep, err := r.store.CreateGiteaEndpoint(ctx, param) + if err != nil { + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to create gitea endpoint") + } + + return ep, nil +} + +func (r *Runner) GetGiteaEndpoint(ctx context.Context, name string) (params.ForgeEndpoint, error) { + if !auth.IsAdmin(ctx) { + return params.ForgeEndpoint{}, runnerErrors.ErrUnauthorized + } + endpoint, err := r.store.GetGiteaEndpoint(ctx, name) + if err != nil { + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to get gitea endpoint") + } + + return endpoint, nil +} + +func (r *Runner) DeleteGiteaEndpoint(ctx context.Context, name string) error { + if !auth.IsAdmin(ctx) { + return runnerErrors.ErrUnauthorized + } + + err := r.store.DeleteGiteaEndpoint(ctx, name) + if err != nil { + return errors.Wrap(err, "failed to delete gitea endpoint") + } + + return nil +} + +func (r *Runner) UpdateGiteaEndpoint(ctx context.Context, name string, param params.UpdateGiteaEndpointParams) (params.ForgeEndpoint, error) { + if !auth.IsAdmin(ctx) { + return params.ForgeEndpoint{}, runnerErrors.ErrUnauthorized + } + + if err := param.Validate(); err != nil { + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to validate gitea endpoint params") + } + + newEp, err := r.store.UpdateGiteaEndpoint(ctx, name, param) + if err != nil { + return params.ForgeEndpoint{}, errors.Wrap(err, "failed to update gitea endpoint") + } + return newEp, nil +} + +func (r *Runner) ListGiteaEndpoints(ctx context.Context) ([]params.ForgeEndpoint, error) { + if !auth.IsAdmin(ctx) { + return nil, runnerErrors.ErrUnauthorized + } + + endpoints, err := r.store.ListGiteaEndpoints(ctx) + if err != nil { + return nil, errors.Wrap(err, "failed to list gitea endpoints") + } + + return endpoints, nil +} diff --git a/runner/github_credentials.go b/runner/github_credentials.go index 7cd4e74c..7c368c99 100644 --- a/runner/github_credentials.go +++ b/runner/github_credentials.go @@ -11,7 +11,7 @@ import ( "github.com/cloudbase/garm/params" ) -func (r *Runner) ListCredentials(ctx context.Context) ([]params.GithubCredentials, error) { +func (r *Runner) ListCredentials(ctx context.Context) ([]params.ForgeCredentials, error) { if !auth.IsAdmin(ctx) { return nil, runnerErrors.ErrUnauthorized } @@ -37,31 +37,31 @@ func (r *Runner) ListCredentials(ctx context.Context) ([]params.GithubCredential return creds, nil } -func (r *Runner) CreateGithubCredentials(ctx context.Context, param params.CreateGithubCredentialsParams) (params.GithubCredentials, error) { +func (r *Runner) CreateGithubCredentials(ctx context.Context, param params.CreateGithubCredentialsParams) (params.ForgeCredentials, error) { if !auth.IsAdmin(ctx) { - return params.GithubCredentials{}, runnerErrors.ErrUnauthorized + return params.ForgeCredentials{}, runnerErrors.ErrUnauthorized } if err := param.Validate(); err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "failed to validate github credentials params") + return params.ForgeCredentials{}, errors.Wrap(err, "failed to validate github credentials params") } creds, err := r.store.CreateGithubCredentials(ctx, param) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "failed to create github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "failed to create github credentials") } return creds, nil } -func (r *Runner) GetGithubCredentials(ctx context.Context, id uint) (params.GithubCredentials, error) { +func (r *Runner) GetGithubCredentials(ctx context.Context, id uint) (params.ForgeCredentials, error) { if !auth.IsAdmin(ctx) { - return params.GithubCredentials{}, runnerErrors.ErrUnauthorized + return params.ForgeCredentials{}, runnerErrors.ErrUnauthorized } creds, err := r.store.GetGithubCredentials(ctx, id, true) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "failed to get github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "failed to get github credentials") } cached, ok := cache.GetGithubCredentials((creds.ID)) @@ -84,18 +84,18 @@ func (r *Runner) DeleteGithubCredentials(ctx context.Context, id uint) error { return nil } -func (r *Runner) UpdateGithubCredentials(ctx context.Context, id uint, param params.UpdateGithubCredentialsParams) (params.GithubCredentials, error) { +func (r *Runner) UpdateGithubCredentials(ctx context.Context, id uint, param params.UpdateGithubCredentialsParams) (params.ForgeCredentials, error) { if !auth.IsAdmin(ctx) { - return params.GithubCredentials{}, runnerErrors.ErrUnauthorized + return params.ForgeCredentials{}, runnerErrors.ErrUnauthorized } if err := param.Validate(); err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "failed to validate github credentials params") + return params.ForgeCredentials{}, errors.Wrap(err, "failed to validate github credentials params") } newCreds, err := r.store.UpdateGithubCredentials(ctx, id, param) if err != nil { - return params.GithubCredentials{}, errors.Wrap(err, "failed to update github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "failed to update github credentials") } return newCreds, nil diff --git a/runner/organizations_test.go b/runner/organizations_test.go index 3609b941..4d4a26e1 100644 --- a/runner/organizations_test.go +++ b/runner/organizations_test.go @@ -39,7 +39,7 @@ type OrgTestFixtures struct { Store dbCommon.Store StoreOrgs map[string]params.Organization Providers map[string]common.Provider - Credentials map[string]params.GithubCredentials + Credentials map[string]params.ForgeCredentials CreateOrgParams params.CreateOrgParams CreatePoolParams params.CreatePoolParams CreateInstanceParams params.CreateInstanceParams @@ -56,8 +56,8 @@ type OrgTestSuite struct { Fixtures *OrgTestFixtures Runner *Runner - testCreds params.GithubCredentials - secondaryTestCreds params.GithubCredentials + testCreds params.ForgeCredentials + secondaryTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint } @@ -104,7 +104,7 @@ func (s *OrgTestSuite) SetupTest() { Providers: map[string]common.Provider{ "test-provider": providerMock, }, - Credentials: map[string]params.GithubCredentials{ + Credentials: map[string]params.ForgeCredentials{ s.testCreds.Name: s.testCreds, s.secondaryTestCreds.Name: s.secondaryTestCreds, }, diff --git a/runner/pool/pool.go b/runner/pool/pool.go index e24aa69b..68de0ec3 100644 --- a/runner/pool/pool.go +++ b/runner/pool/pool.go @@ -83,7 +83,7 @@ func NewEntityPoolManager(ctx context.Context, entity params.ForgeEntity, instan return nil, errors.Wrap(err, "getting controller info") } - consumerID := fmt.Sprintf("pool-manager-%s-%s", entity.String(), entity.Credentials.Endpoint().Name) + consumerID := fmt.Sprintf("pool-manager-%s-%s", entity.String(), entity.Credentials.Endpoint.Name) slog.InfoContext(ctx, "registering consumer", "consumer_id", consumerID) consumer, err := watcher.RegisterConsumer( ctx, consumerID, @@ -887,7 +887,7 @@ func (r *basePoolManager) addInstanceToProvider(instance params.Instance) error Image: pool.Image, ExtraSpecs: pool.ExtraSpecs, PoolID: instance.PoolID, - CACertBundle: r.entity.Credentials.CABundle(), + CACertBundle: r.entity.Credentials.CABundle, GitHubRunnerGroup: instance.GitHubRunnerGroup, JitConfigEnabled: hasJITConfig, } @@ -1366,6 +1366,19 @@ func (r *basePoolManager) deleteInstanceFromProvider(ctx context.Context, instan return nil } +func (r *basePoolManager) sleepWithCancel(sleepTime time.Duration) (canceled bool) { + ticker := time.NewTicker(sleepTime) + defer ticker.Stop() + + select { + case <-ticker.C: + return false + case <-r.quit: + case <-r.ctx.Done(): + } + return true +} + func (r *basePoolManager) deletePendingInstances() error { instances, err := r.store.ListEntityInstances(r.ctx, r.entity) if err != nil { @@ -1414,7 +1427,9 @@ func (r *basePoolManager) deletePendingInstances() error { return fmt.Errorf("failed to generate random number: %w", err) } jitter := time.Duration(num.Int64()) * time.Millisecond - time.Sleep(jitter) + if canceled := r.sleepWithCancel(jitter); canceled { + return nil + } currentStatus := instance.Status deleteMux := false diff --git a/runner/pool/watcher.go b/runner/pool/watcher.go index 56427e89..455f4239 100644 --- a/runner/pool/watcher.go +++ b/runner/pool/watcher.go @@ -125,12 +125,12 @@ func (r *basePoolManager) handleWatcherEvent(event common.ChangePayload) { dbEntityType := common.DatabaseEntityType(r.entity.EntityType) switch event.EntityType { case common.GithubCredentialsEntityType: - credentials, ok := event.Payload.(params.GithubCredentials) + credentials, ok := event.Payload.(params.ForgeCredentials) if !ok { slog.ErrorContext(r.ctx, "failed to cast payload to github credentials") return } - r.handleCredentialsUpdate(credentials.GetForgeCredentials()) + r.handleCredentialsUpdate(credentials) case common.ControllerEntityType: controllerInfo, ok := event.Payload.(params.ControllerInfo) if !ok { diff --git a/runner/pools_test.go b/runner/pools_test.go index 95c6b6bd..587addce 100644 --- a/runner/pools_test.go +++ b/runner/pools_test.go @@ -47,8 +47,8 @@ type PoolTestSuite struct { Runner *Runner adminCtx context.Context - testCreds params.GithubCredentials - secondaryTestCreds params.GithubCredentials + testCreds params.ForgeCredentials + secondaryTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint } diff --git a/runner/repositories.go b/runner/repositories.go index 83876c5d..058e1a02 100644 --- a/runner/repositories.go +++ b/runner/repositories.go @@ -38,7 +38,16 @@ func (r *Runner) CreateRepository(ctx context.Context, param params.CreateRepoPa return params.Repository{}, errors.Wrap(err, "validating params") } - creds, err := r.store.GetGithubCredentialsByName(ctx, param.CredentialsName, true) + var creds params.ForgeCredentials + switch param.GetForgeType() { + case params.GithubEndpointType: + creds, err = r.store.GetGithubCredentialsByName(ctx, param.CredentialsName, true) + case params.GiteaEndpointType: + creds, err = r.store.GetGiteaCredentialsByName(ctx, param.CredentialsName, true) + default: + return params.Repository{}, runnerErrors.NewBadRequestError("invalid forge type: %s", param.GetForgeType()) + } + if err != nil { return params.Repository{}, runnerErrors.NewBadRequestError("credentials %s not defined", param.CredentialsName) } @@ -52,7 +61,7 @@ func (r *Runner) CreateRepository(ctx context.Context, param params.CreateRepoPa return params.Repository{}, runnerErrors.NewConflictError("repository %s/%s already exists", param.Owner, param.Name) } - repo, err = r.store.CreateRepository(ctx, param.Owner, param.Name, creds.Name, param.WebhookSecret, param.PoolBalancerType) + repo, err = r.store.CreateRepository(ctx, param.Owner, param.Name, creds, param.WebhookSecret, param.PoolBalancerType) if err != nil { return params.Repository{}, errors.Wrap(err, "creating repository") } diff --git a/runner/repositories_test.go b/runner/repositories_test.go index 47bfb003..4e891e4b 100644 --- a/runner/repositories_test.go +++ b/runner/repositories_test.go @@ -39,7 +39,7 @@ type RepoTestFixtures struct { Store dbCommon.Store StoreRepos map[string]params.Repository Providers map[string]common.Provider - Credentials map[string]params.GithubCredentials + Credentials map[string]params.ForgeCredentials CreateRepoParams params.CreateRepoParams CreatePoolParams params.CreatePoolParams CreateInstanceParams params.CreateInstanceParams @@ -60,8 +60,8 @@ type RepoTestSuite struct { Fixtures *RepoTestFixtures Runner *Runner - testCreds params.GithubCredentials - secondaryTestCreds params.GithubCredentials + testCreds params.ForgeCredentials + secondaryTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint } @@ -86,7 +86,7 @@ func (s *RepoTestSuite) SetupTest() { adminCtx, fmt.Sprintf("test-owner-%v", i), name, - s.testCreds.Name, + s.testCreds, fmt.Sprintf("test-webhook-secret-%v", i), params.PoolBalancerTypeRoundRobin, ) @@ -107,7 +107,7 @@ func (s *RepoTestSuite) SetupTest() { Providers: map[string]common.Provider{ "test-provider": providerMock, }, - Credentials: map[string]params.GithubCredentials{ + Credentials: map[string]params.ForgeCredentials{ s.testCreds.Name: s.testCreds, s.secondaryTestCreds.Name: s.secondaryTestCreds, }, diff --git a/test/integration/client_utils.go b/test/integration/client_utils.go index 977cc11c..c986be2d 100644 --- a/test/integration/client_utils.go +++ b/test/integration/client_utils.go @@ -51,7 +51,7 @@ func listCredentials(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfo return listCredentialsResponse.Payload, nil } -func createGithubCredentials(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, credentialsParams params.CreateGithubCredentialsParams) (*params.GithubCredentials, error) { +func createGithubCredentials(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, credentialsParams params.CreateGithubCredentialsParams) (*params.ForgeCredentials, error) { createCredentialsResponse, err := apiCli.Credentials.CreateCredentials( clientCredentials.NewCreateCredentialsParams().WithBody(credentialsParams), apiAuthToken) @@ -67,7 +67,7 @@ func deleteGithubCredentials(apiCli *client.GarmAPI, apiAuthToken runtime.Client apiAuthToken) } -func updateGithubCredentials(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, credentialsID int64, credentialsParams params.UpdateGithubCredentialsParams) (*params.GithubCredentials, error) { +func updateGithubCredentials(apiCli *client.GarmAPI, apiAuthToken runtime.ClientAuthInfoWriter, credentialsID int64, credentialsParams params.UpdateGithubCredentialsParams) (*params.ForgeCredentials, error) { updateCredentialsResponse, err := apiCli.Credentials.UpdateCredentials( clientCredentials.NewUpdateCredentialsParams().WithID(credentialsID).WithBody(credentialsParams), apiAuthToken) diff --git a/test/integration/credentials_test.go b/test/integration/credentials_test.go index f7c9c691..f1594f87 100644 --- a/test/integration/credentials_test.go +++ b/test/integration/credentials_test.go @@ -199,7 +199,7 @@ func (suite *GarmSuite) TestGithubCredentialsFailsOnDuplicateName() { expectAPIStatusCode(err, 409) } -func (suite *GarmSuite) createDummyCredentials(name, endpointName string) (*params.GithubCredentials, error) { +func (suite *GarmSuite) createDummyCredentials(name, endpointName string) (*params.ForgeCredentials, error) { createCredsParams := params.CreateGithubCredentialsParams{ Name: name, Endpoint: endpointName, @@ -212,7 +212,7 @@ func (suite *GarmSuite) createDummyCredentials(name, endpointName string) (*para return suite.CreateGithubCredentials(createCredsParams) } -func (suite *GarmSuite) CreateGithubCredentials(credentialsParams params.CreateGithubCredentialsParams) (*params.GithubCredentials, error) { +func (suite *GarmSuite) CreateGithubCredentials(credentialsParams params.CreateGithubCredentialsParams) (*params.ForgeCredentials, error) { t := suite.T() t.Log("Create GitHub credentials") credentials, err := createGithubCredentials(suite.cli, suite.authToken, credentialsParams) diff --git a/util/github/client.go b/util/github/client.go index 1480561a..bcdebc13 100644 --- a/util/github/client.go +++ b/util/github/client.go @@ -477,6 +477,12 @@ func (g *githubClient) GetEntityJITConfig(ctx context.Context, instance string, func (g *githubClient) RateLimit(ctx context.Context) (*github.RateLimits, error) { limits, resp, err := g.rateLimit.Get(ctx) + if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "GetRateLimit", // label: operation + g.entity.LabelScope(), // label: scope + ).Inc() + } if err := parseError(resp, err); err != nil { return nil, fmt.Errorf("getting rate limit: %w", err) } @@ -491,7 +497,7 @@ func (g *githubClient) GithubBaseURL() *url.URL { return g.cli.BaseURL } -func NewRateLimitClient(ctx context.Context, credentials params.GithubCredentials) (common.RateLimitClient, error) { +func NewRateLimitClient(ctx context.Context, credentials params.ForgeCredentials) (common.RateLimitClient, error) { httpClient, err := credentials.GetHTTPClient(ctx) if err != nil { return nil, errors.Wrap(err, "fetching http client") @@ -515,12 +521,12 @@ func NewRateLimitClient(ctx context.Context, credentials params.GithubCredential return cli, nil } -func withGiteaURLs(client *github.Client, apiBaseURL, uploadBaseURL string) (*github.Client, error) { +func withGiteaURLs(client *github.Client, apiBaseURL string) (*github.Client, error) { if client == nil { return nil, errors.New("client is nil") } - if apiBaseURL == "" || uploadBaseURL == "" { + if apiBaseURL == "" { return nil, errors.New("invalid gitea URLs") } @@ -537,21 +543,8 @@ func withGiteaURLs(client *github.Client, apiBaseURL, uploadBaseURL string) (*gi parsedBaseURL.Path += "api/v1/" } - parsedUploadURL, err := url.ParseRequestURI(uploadBaseURL) - if err != nil { - return nil, errors.Wrap(err, "parsing gitea upload URL") - } - - if !strings.HasSuffix(parsedUploadURL.Path, "/") { - parsedUploadURL.Path += "/" - } - - if !strings.HasSuffix(parsedUploadURL.Path, "/api/v1/") { - parsedUploadURL.Path += "api/v1/" - } - client.BaseURL = parsedBaseURL - client.UploadURL = parsedUploadURL + client.UploadURL = parsedBaseURL return client, nil } @@ -565,15 +558,15 @@ func Client(ctx context.Context, entity params.ForgeEntity) (common.GithubClient slog.DebugContext( ctx, "creating client for entity", - "entity", entity.String(), "base_url", entity.Credentials.APIBaseURL(), - "upload_url", entity.Credentials.UploadBaseURL()) + "entity", entity.String(), "base_url", entity.Credentials.APIBaseURL, + "upload_url", entity.Credentials.UploadBaseURL) ghClient := github.NewClient(httpClient) - switch entity.Credentials.ForgeType { + switch entity.Credentials.Endpoint.EndpointType { case params.GithubEndpointType: - ghClient, err = ghClient.WithEnterpriseURLs(entity.Credentials.APIBaseURL(), entity.Credentials.UploadBaseURL()) + ghClient, err = ghClient.WithEnterpriseURLs(entity.Credentials.APIBaseURL, entity.Credentials.UploadBaseURL) case params.GiteaEndpointType: - ghClient, err = withGiteaURLs(ghClient, entity.Credentials.APIBaseURL(), entity.Credentials.UploadBaseURL()) + ghClient, err = withGiteaURLs(ghClient, entity.Credentials.APIBaseURL) } if err != nil { diff --git a/vendor/golang.org/x/mod/LICENSE b/vendor/golang.org/x/mod/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/vendor/golang.org/x/mod/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/mod/PATENTS b/vendor/golang.org/x/mod/PATENTS new file mode 100644 index 00000000..73309904 --- /dev/null +++ b/vendor/golang.org/x/mod/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/mod/semver/semver.go b/vendor/golang.org/x/mod/semver/semver.go new file mode 100644 index 00000000..9a2dfd33 --- /dev/null +++ b/vendor/golang.org/x/mod/semver/semver.go @@ -0,0 +1,401 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package semver implements comparison of semantic version strings. +// In this package, semantic version strings must begin with a leading "v", +// as in "v1.0.0". +// +// The general form of a semantic version string accepted by this package is +// +// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] +// +// where square brackets indicate optional parts of the syntax; +// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; +// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers +// using only alphanumeric characters and hyphens; and +// all-numeric PRERELEASE identifiers must not have leading zeros. +// +// This package follows Semantic Versioning 2.0.0 (see semver.org) +// with two exceptions. First, it requires the "v" prefix. Second, it recognizes +// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) +// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. +package semver + +import "sort" + +// parsed returns the parsed form of a semantic version string. +type parsed struct { + major string + minor string + patch string + short string + prerelease string + build string +} + +// IsValid reports whether v is a valid semantic version string. +func IsValid(v string) bool { + _, ok := parse(v) + return ok +} + +// Canonical returns the canonical formatting of the semantic version v. +// It fills in any missing .MINOR or .PATCH and discards build metadata. +// Two semantic versions compare equal only if their canonical formattings +// are identical strings. +// The canonical invalid semantic version is the empty string. +func Canonical(v string) string { + p, ok := parse(v) + if !ok { + return "" + } + if p.build != "" { + return v[:len(v)-len(p.build)] + } + if p.short != "" { + return v + p.short + } + return v +} + +// Major returns the major version prefix of the semantic version v. +// For example, Major("v2.1.0") == "v2". +// If v is an invalid semantic version string, Major returns the empty string. +func Major(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return v[:1+len(pv.major)] +} + +// MajorMinor returns the major.minor version prefix of the semantic version v. +// For example, MajorMinor("v2.1.0") == "v2.1". +// If v is an invalid semantic version string, MajorMinor returns the empty string. +func MajorMinor(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + i := 1 + len(pv.major) + if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { + return v[:j] + } + return v[:i] + "." + pv.minor +} + +// Prerelease returns the prerelease suffix of the semantic version v. +// For example, Prerelease("v2.1.0-pre+meta") == "-pre". +// If v is an invalid semantic version string, Prerelease returns the empty string. +func Prerelease(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return pv.prerelease +} + +// Build returns the build suffix of the semantic version v. +// For example, Build("v2.1.0+meta") == "+meta". +// If v is an invalid semantic version string, Build returns the empty string. +func Build(v string) string { + pv, ok := parse(v) + if !ok { + return "" + } + return pv.build +} + +// Compare returns an integer comparing two versions according to +// semantic version precedence. +// The result will be 0 if v == w, -1 if v < w, or +1 if v > w. +// +// An invalid semantic version string is considered less than a valid one. +// All invalid semantic version strings compare equal to each other. +func Compare(v, w string) int { + pv, ok1 := parse(v) + pw, ok2 := parse(w) + if !ok1 && !ok2 { + return 0 + } + if !ok1 { + return -1 + } + if !ok2 { + return +1 + } + if c := compareInt(pv.major, pw.major); c != 0 { + return c + } + if c := compareInt(pv.minor, pw.minor); c != 0 { + return c + } + if c := compareInt(pv.patch, pw.patch); c != 0 { + return c + } + return comparePrerelease(pv.prerelease, pw.prerelease) +} + +// Max canonicalizes its arguments and then returns the version string +// that compares greater. +// +// Deprecated: use [Compare] instead. In most cases, returning a canonicalized +// version is not expected or desired. +func Max(v, w string) string { + v = Canonical(v) + w = Canonical(w) + if Compare(v, w) > 0 { + return v + } + return w +} + +// ByVersion implements [sort.Interface] for sorting semantic version strings. +type ByVersion []string + +func (vs ByVersion) Len() int { return len(vs) } +func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } +func (vs ByVersion) Less(i, j int) bool { + cmp := Compare(vs[i], vs[j]) + if cmp != 0 { + return cmp < 0 + } + return vs[i] < vs[j] +} + +// Sort sorts a list of semantic version strings using [ByVersion]. +func Sort(list []string) { + sort.Sort(ByVersion(list)) +} + +func parse(v string) (p parsed, ok bool) { + if v == "" || v[0] != 'v' { + return + } + p.major, v, ok = parseInt(v[1:]) + if !ok { + return + } + if v == "" { + p.minor = "0" + p.patch = "0" + p.short = ".0.0" + return + } + if v[0] != '.' { + ok = false + return + } + p.minor, v, ok = parseInt(v[1:]) + if !ok { + return + } + if v == "" { + p.patch = "0" + p.short = ".0" + return + } + if v[0] != '.' { + ok = false + return + } + p.patch, v, ok = parseInt(v[1:]) + if !ok { + return + } + if len(v) > 0 && v[0] == '-' { + p.prerelease, v, ok = parsePrerelease(v) + if !ok { + return + } + } + if len(v) > 0 && v[0] == '+' { + p.build, v, ok = parseBuild(v) + if !ok { + return + } + } + if v != "" { + ok = false + return + } + ok = true + return +} + +func parseInt(v string) (t, rest string, ok bool) { + if v == "" { + return + } + if v[0] < '0' || '9' < v[0] { + return + } + i := 1 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + if v[0] == '0' && i != 1 { + return + } + return v[:i], v[i:], true +} + +func parsePrerelease(v string) (t, rest string, ok bool) { + // "A pre-release version MAY be denoted by appending a hyphen and + // a series of dot separated identifiers immediately following the patch version. + // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. + // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." + if v == "" || v[0] != '-' { + return + } + i := 1 + start := 1 + for i < len(v) && v[i] != '+' { + if !isIdentChar(v[i]) && v[i] != '.' { + return + } + if v[i] == '.' { + if start == i || isBadNum(v[start:i]) { + return + } + start = i + 1 + } + i++ + } + if start == i || isBadNum(v[start:i]) { + return + } + return v[:i], v[i:], true +} + +func parseBuild(v string) (t, rest string, ok bool) { + if v == "" || v[0] != '+' { + return + } + i := 1 + start := 1 + for i < len(v) { + if !isIdentChar(v[i]) && v[i] != '.' { + return + } + if v[i] == '.' { + if start == i { + return + } + start = i + 1 + } + i++ + } + if start == i { + return + } + return v[:i], v[i:], true +} + +func isIdentChar(c byte) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' +} + +func isBadNum(v string) bool { + i := 0 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + return i == len(v) && i > 1 && v[0] == '0' +} + +func isNum(v string) bool { + i := 0 + for i < len(v) && '0' <= v[i] && v[i] <= '9' { + i++ + } + return i == len(v) +} + +func compareInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} + +func comparePrerelease(x, y string) int { + // "When major, minor, and patch are equal, a pre-release version has + // lower precedence than a normal version. + // Example: 1.0.0-alpha < 1.0.0. + // Precedence for two pre-release versions with the same major, minor, + // and patch version MUST be determined by comparing each dot separated + // identifier from left to right until a difference is found as follows: + // identifiers consisting of only digits are compared numerically and + // identifiers with letters or hyphens are compared lexically in ASCII + // sort order. Numeric identifiers always have lower precedence than + // non-numeric identifiers. A larger set of pre-release fields has a + // higher precedence than a smaller set, if all of the preceding + // identifiers are equal. + // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < + // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." + if x == y { + return 0 + } + if x == "" { + return +1 + } + if y == "" { + return -1 + } + for x != "" && y != "" { + x = x[1:] // skip - or . + y = y[1:] // skip - or . + var dx, dy string + dx, x = nextIdent(x) + dy, y = nextIdent(y) + if dx != dy { + ix := isNum(dx) + iy := isNum(dy) + if ix != iy { + if ix { + return -1 + } else { + return +1 + } + } + if ix { + if len(dx) < len(dy) { + return -1 + } + if len(dx) > len(dy) { + return +1 + } + } + if dx < dy { + return -1 + } else { + return +1 + } + } + } + if x == "" { + return -1 + } else { + return +1 + } +} + +func nextIdent(x string) (dx, rest string) { + i := 0 + for i < len(x) && x[i] != '.' { + i++ + } + return x[:i], x[i:] +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5cb70bb1..dbd42ce3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -286,6 +286,9 @@ golang.org/x/crypto/chacha20poly1305 golang.org/x/crypto/hkdf golang.org/x/crypto/internal/alias golang.org/x/crypto/internal/poly1305 +# golang.org/x/mod v0.17.0 +## explicit; go 1.18 +golang.org/x/mod/semver # golang.org/x/net v0.40.0 ## explicit; go 1.23.0 golang.org/x/net/internal/socks diff --git a/workers/cache/cache.go b/workers/cache/cache.go index ce23d269..918b3438 100644 --- a/workers/cache/cache.go +++ b/workers/cache/cache.go @@ -138,7 +138,7 @@ func (w *Worker) loadAllInstances() error { return nil } -func (w *Worker) loadAllCredentials() error { +func (w *Worker) loadAllGithubCredentials() error { creds, err := w.store.ListGithubCredentials(w.ctx) if err != nil { return fmt.Errorf("listing github credentials: %w", err) @@ -150,6 +150,18 @@ func (w *Worker) loadAllCredentials() error { return nil } +func (w *Worker) loadAllGiteaCredentials() error { + creds, err := w.store.ListGiteaCredentials(w.ctx) + if err != nil { + return fmt.Errorf("listing gitea credentials: %w", err) + } + + for _, cred := range creds { + cache.SetGiteaCredentials(cred) + } + return nil +} + func (w *Worker) waitForErrorGroupOrContextCancelled(g *errgroup.Group) error { if g == nil { return nil @@ -182,6 +194,20 @@ func (w *Worker) Start() error { g, _ := errgroup.WithContext(w.ctx) + g.Go(func() error { + if err := w.loadAllGithubCredentials(); err != nil { + return fmt.Errorf("loading all github credentials: %w", err) + } + return nil + }) + + g.Go(func() error { + if err := w.loadAllGiteaCredentials(); err != nil { + return fmt.Errorf("loading all gitea credentials: %w", err) + } + return nil + }) + g.Go(func() error { if err := w.loadAllEntities(); err != nil { return fmt.Errorf("loading all entities: %w", err) @@ -196,13 +222,6 @@ func (w *Worker) Start() error { return nil }) - g.Go(func() error { - if err := w.loadAllCredentials(); err != nil { - return fmt.Errorf("loading all credentials: %w", err) - } - return nil - }) - if err := w.waitForErrorGroupOrContextCancelled(g); err != nil { return fmt.Errorf("waiting for error group: %w", err) } @@ -360,7 +379,7 @@ func (w *Worker) handleInstanceEvent(event common.ChangePayload) { } func (w *Worker) handleCredentialsEvent(event common.ChangePayload) { - credentials, ok := event.Payload.(params.GithubCredentials) + credentials, ok := event.Payload.(params.ForgeCredentials) if !ok { slog.DebugContext(w.ctx, "invalid payload type for credentials event", "payload", event.Payload) return diff --git a/workers/cache/gitea_tools.go b/workers/cache/gitea_tools.go new file mode 100644 index 00000000..8b2fc758 --- /dev/null +++ b/workers/cache/gitea_tools.go @@ -0,0 +1,152 @@ +package cache + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" + + commonParams "github.com/cloudbase/garm-provider-common/params" + "golang.org/x/mod/semver" +) + +const ( + // GiteaRunnerReleasesURL is the public API URL that returns a json of all Gitea runner releases. + // By default it returns the last 10 releases, which is enough for our needs. + GiteaRunnerReleasesURL = "https://gitea.com/api/v1/repos/gitea/act_runner/releases" + // GiteaRunnerMinimumVersion is the minimum version we need in order to support ephemeral runners. + GiteaRunnerMinimumVersion = "v0.2.12" +) + +var nightlyActRunner = GiteaEntityTool{ + TagName: "nightly", + Name: "nightly", + TarballURL: "https://gitea.com/gitea/act_runner/archive/main.tar.gz", + Assets: []GiteaToolsAssets{ + { + Name: "act_runner-nightly-linux-amd64.xz", + DownloadURL: "https://dl.gitea.com/act_runner/nightly/act_runner-nightly-linux-amd64.xz", + }, + { + Name: "act_runner-nightly-linux-arm64.xz", + DownloadURL: "https://dl.gitea.com/act_runner/nightly/act_runner-nightly-linux-arm64.xz", + }, + { + Name: "act_runner-nightly-windows-amd64.exe.xz", + DownloadURL: "https://dl.gitea.com/act_runner/nightly/act_runner-nightly-windows-amd64.exe.xz", + }, + }, +} + +type GiteaToolsAssets struct { + ID uint `json:"id"` + Name string `json:"name"` + Size uint `json:"size"` + DownloadCount uint `json:"download_count"` + CreatedAt time.Time `json:"created_at"` + UUID string `json:"uuid"` + DownloadURL string `json:"browser_download_url"` +} + +func (g GiteaToolsAssets) GetOS() *string { + if g.Name == "" { + return nil + } + + parts := strings.SplitN(g.Name, "-", 4) + if len(parts) != 4 { + return nil + } + + os := parts[2] + return &os +} + +func (g GiteaToolsAssets) GetArch() *string { + if g.Name == "" { + return nil + } + + parts := strings.SplitN(g.Name, "-", 4) + if len(parts) != 4 { + return nil + } + + archParts := strings.SplitN(parts[3], ".", 2) + if len(archParts) == 0 { + return nil + } + arch := archParts[0] + return &arch +} + +type GiteaEntityTool struct { + // TagName is the semver version of the release. + TagName string `json:"tag_name"` + Name string `json:"name"` + TarballURL string `json:"tarball_url"` + Assets []GiteaToolsAssets `json:"assets"` +} + +type GiteaEntityTools []GiteaEntityTool + +func (g GiteaEntityTools) GetLatestVersion() string { + if len(g) == 0 { + return "" + } + return g[0].TagName +} + +func (g GiteaEntityTools) MinimumVersion() (GiteaEntityTool, bool) { + if len(g) == 0 { + return GiteaEntityTool{}, false + } + for _, tool := range g { + if semver.Compare(tool.TagName, GiteaRunnerMinimumVersion) >= 0 { + return tool, true + } + } + return GiteaEntityTool{}, false +} + +func getTools() ([]commonParams.RunnerApplicationDownload, error) { + resp, err := http.Get(GiteaRunnerReleasesURL) + if err != nil { + return nil, err + } + defer resp.Body.Close() + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var tools GiteaEntityTools + err = json.Unmarshal(data, &tools) + if err != nil { + return nil, err + } + + if len(tools) == 0 { + return nil, fmt.Errorf("no tools found") + } + + latest, ok := tools.MinimumVersion() + if !ok { + latest = nightlyActRunner + } + + ret := []commonParams.RunnerApplicationDownload{} + + for _, asset := range latest.Assets { + ret = append(ret, commonParams.RunnerApplicationDownload{ + OS: asset.GetOS(), + Architecture: asset.GetArch(), + DownloadURL: &asset.DownloadURL, + Filename: &asset.Name, + }) + } + + return ret, nil +} diff --git a/workers/cache/tool_cache.go b/workers/cache/tool_cache.go index 7d8d5737..d3c74673 100644 --- a/workers/cache/tool_cache.go +++ b/workers/cache/tool_cache.go @@ -49,7 +49,14 @@ func (t *toolsUpdater) Start() error { t.running = true t.quit = make(chan struct{}) - go t.loop() + slog.DebugContext(t.ctx, "starting tools updater", "entity", t.entity.String(), "forge_type", t.entity.Credentials) + + switch t.entity.Credentials.ForgeType { + case params.GithubEndpointType: + go t.loop() + case params.GiteaEndpointType: + go t.giteaUpdateLoop() + } return nil } @@ -68,7 +75,7 @@ func (t *toolsUpdater) Stop() error { } func (t *toolsUpdater) updateTools() error { - slog.DebugContext(t.ctx, "updating tools", "entity", t.entity.String()) + slog.DebugContext(t.ctx, "updating tools", "entity", t.entity.String(), "forge_type", t.entity.Credentials.ForgeType) entity, ok := cache.GetEntity(t.entity.ID) if !ok { return fmt.Errorf("getting entity from cache: %s", t.entity.ID) @@ -98,12 +105,69 @@ func (t *toolsUpdater) Reset() { return } + if t.entity.Credentials.ForgeType == params.GiteaEndpointType { + // no need to reset the gitea tools updater when credentials + // are updated. + return + } + if t.reset != nil { close(t.reset) t.reset = nil } } +func (t *toolsUpdater) sleepWithCancel(sleepTime time.Duration) (canceled bool) { + ticker := time.NewTicker(sleepTime) + defer ticker.Stop() + + select { + case <-ticker.C: + return false + case <-t.quit: + case <-t.ctx.Done(): + } + return true +} + +// giteaUpdateLoop updates tools for gitea. The act runner can be downloaded +// without a token, unlike the github tools, which for GHES require a token. +func (t *toolsUpdater) giteaUpdateLoop() { + defer t.Stop() + + // add some jitter. When spinning up multiple entities, we add + // jitter to prevent stampeeding herd. + randInt, err := rand.Int(rand.Reader, big.NewInt(3000)) + if err != nil { + randInt = big.NewInt(0) + } + t.sleepWithCancel(time.Duration(randInt.Int64()) * time.Millisecond) + tools, err := getTools() + if err == nil { + cache.SetGithubToolsCache(t.entity, tools) + } + + // Once every 3 hours should be enough. Tools don't expire. + ticker := time.NewTicker(3 * time.Hour) + + for { + select { + case <-t.quit: + slog.DebugContext(t.ctx, "stopping tools updater") + return + case <-t.ctx.Done(): + return + case <-ticker.C: + tools, err := getTools() + if err != nil { + slog.DebugContext(t.ctx, "failed to update gitea tools", "error", err) + continue + } + cache.SetGithubToolsCache(t.entity, tools) + } + } +} + func (t *toolsUpdater) loop() { defer t.Stop() @@ -113,7 +177,7 @@ func (t *toolsUpdater) loop() { if err != nil { randInt = big.NewInt(0) } - time.Sleep(time.Duration(randInt.Int64()) * time.Millisecond) + t.sleepWithCancel(time.Duration(randInt.Int64()) * time.Millisecond) var resetTime time.Time now := time.Now().UTC() diff --git a/workers/entity/controller.go b/workers/entity/controller.go index db353f0e..2cb910b3 100644 --- a/workers/entity/controller.go +++ b/workers/entity/controller.go @@ -215,6 +215,7 @@ func (c *Controller) Stop() error { c.running = false close(c.quit) c.consumer.Close() + slog.DebugContext(c.ctx, "stopped entity controller", "entity", c.consumerID) return nil } diff --git a/workers/entity/worker.go b/workers/entity/worker.go index 7f0f79e6..597d5797 100644 --- a/workers/entity/worker.go +++ b/workers/entity/worker.go @@ -70,6 +70,7 @@ func (w *Worker) Stop() error { w.running = false close(w.quit) w.consumer.Close() + slog.DebugContext(w.ctx, "entity worker stopped", "entity", w.consumerID) return nil } diff --git a/workers/entity/worker_watcher.go b/workers/entity/worker_watcher.go index 04e20a65..f445b73d 100644 --- a/workers/entity/worker_watcher.go +++ b/workers/entity/worker_watcher.go @@ -63,12 +63,11 @@ func (w *Worker) handleEntityEventPayload(event dbCommon.ChangePayload) { } func (w *Worker) handleEntityCredentialsEventPayload(event dbCommon.ChangePayload) { - var credsGetter params.ForgeCredentialsGetter + var creds params.ForgeCredentials var ok bool - switch event.EntityType { case dbCommon.GithubCredentialsEntityType: - credsGetter, ok = event.Payload.(params.GithubCredentials) + creds, ok = event.Payload.(params.ForgeCredentials) default: slog.ErrorContext(w.ctx, "invalid entity type", "entity_type", event.EntityType) return @@ -78,7 +77,7 @@ func (w *Worker) handleEntityCredentialsEventPayload(event dbCommon.ChangePayloa return } - credentials := credsGetter.GetForgeCredentials() + credentials := creds switch event.Operation { case dbCommon.UpdateOperation: diff --git a/workers/provider/instance_manager.go b/workers/provider/instance_manager.go index 9ba94553..47e875a0 100644 --- a/workers/provider/instance_manager.go +++ b/workers/provider/instance_manager.go @@ -167,7 +167,7 @@ func (i *instanceManager) handleCreateInstanceInProvider(instance params.Instanc ExtraSpecs: i.scaleSet.ExtraSpecs, // This is temporary. We need to extend providers to know about scale sets. PoolID: i.pseudoPoolID(), - CACertBundle: entity.Credentials.CABundle(), + CACertBundle: entity.Credentials.CABundle, GitHubRunnerGroup: i.scaleSet.GitHubRunnerGroup, JitConfigEnabled: true, } diff --git a/workers/scaleset/controller.go b/workers/scaleset/controller.go index 45dfbfa3..e28eb7ee 100644 --- a/workers/scaleset/controller.go +++ b/workers/scaleset/controller.go @@ -137,7 +137,7 @@ func (c *Controller) Stop() error { c.running = false close(c.quit) c.consumer.Close() - + slog.DebugContext(c.ctx, "stopped scale set controller", "entity", c.Entity.String()) return nil } diff --git a/workers/scaleset/scaleset.go b/workers/scaleset/scaleset.go index 097a8680..1090388d 100644 --- a/workers/scaleset/scaleset.go +++ b/workers/scaleset/scaleset.go @@ -910,7 +910,7 @@ func (w *Worker) handleAutoScale() { } if !hasTools { - time.Sleep(1 * time.Second) + w.sleepWithCancel(1 * time.Second) continue } From 8538a4ae8ad1fbb652671323a82ed3b903b34c44 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 14 May 2025 15:22:27 +0000 Subject: [PATCH 05/21] Make sure websocket hub exits properly Signed-off-by: Gabriel Adrian Samfira --- websocket/client.go | 4 ++-- websocket/websocket.go | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/websocket/client.go b/websocket/client.go index 5b80ba81..657aa49e 100644 --- a/websocket/client.go +++ b/websocket/client.go @@ -58,8 +58,6 @@ func NewClient(ctx context.Context, conn *websocket.Conn) (*Client, error) { userID: user, passwordGeneration: generation, consumer: consumer, - done: make(chan struct{}), - send: make(chan []byte, 100), }, nil } @@ -116,6 +114,8 @@ func (c *Client) Start() error { defer c.mux.Unlock() c.running = true + c.send = make(chan []byte, 100) + c.done = make(chan struct{}) go c.runWatcher() go c.clientReader() diff --git a/websocket/websocket.go b/websocket/websocket.go index 57820449..14b5e785 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -28,14 +28,15 @@ type Hub struct { // Inbound messages from the clients. broadcast chan []byte - mux sync.Mutex - once sync.Once + mux sync.Mutex + running bool + once sync.Once } func (h *Hub) run() { - defer func() { - close(h.closed) - }() + defer close(h.closed) + defer h.Stop() + for { select { case <-h.quit: @@ -59,8 +60,7 @@ func (h *Hub) run() { for _, id := range staleClients { if client, ok := h.clients[id]; ok { if client != nil { - client.conn.Close() - close(client.send) + client.Stop() } delete(h.clients, id) } @@ -105,6 +105,13 @@ func (h *Hub) Unregister(client *Client) error { } func (h *Hub) Write(msg []byte) (int, error) { + h.mux.Lock() + if !h.running { + h.mux.Unlock() + return 0, fmt.Errorf("websocket writer is not running") + } + h.mux.Unlock() + tmp := make([]byte, len(msg)) copy(tmp, msg) timer := time.NewTimer(5 * time.Second) @@ -118,6 +125,15 @@ func (h *Hub) Write(msg []byte) (int, error) { } func (h *Hub) Start() error { + h.mux.Lock() + defer h.mux.Unlock() + + if h.running { + return nil + } + + h.running = true + go h.run() return nil } @@ -130,11 +146,22 @@ func (h *Hub) Close() error { } func (h *Hub) Stop() error { + h.mux.Lock() + defer h.mux.Unlock() + + if !h.running { + return nil + } + + h.running = false h.Close() return h.Wait() } func (h *Hub) Wait() error { + if !h.running { + return nil + } timer := time.NewTimer(60 * time.Second) defer timer.Stop() select { From 56be5eb698ae251ef211174529aec662655b4f4d Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 14 May 2025 15:36:53 +0000 Subject: [PATCH 06/21] Do not load scalesets and runners in parallel Both functions read and write to the same map. We should switch to sync.Map Signed-off-by: Gabriel Adrian Samfira --- database/sql/models.go | 6 +++--- workers/provider/provider.go | 24 +++++------------------- workers/provider/util.go | 23 ----------------------- 3 files changed, 8 insertions(+), 45 deletions(-) diff --git a/database/sql/models.go b/database/sql/models.go index 0ff2d8f4..e7fad261 100644 --- a/database/sql/models.go +++ b/database/sql/models.go @@ -171,7 +171,7 @@ type Repository struct { EndpointName *string `gorm:"index:idx_owner_nocase,unique,collate:nocase"` Endpoint GithubEndpoint `gorm:"foreignKey:EndpointName;constraint:OnDelete:SET NULL"` - Events []RepositoryEvent `gorm:"foreignKey:RepoID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"` + Events []*RepositoryEvent `gorm:"foreignKey:RepoID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"` } type OrganizationEvent struct { @@ -203,7 +203,7 @@ type Organization struct { EndpointName *string `gorm:"index:idx_org_name_nocase,collate:nocase"` Endpoint GithubEndpoint `gorm:"foreignKey:EndpointName;constraint:OnDelete:SET NULL"` - Events []OrganizationEvent `gorm:"foreignKey:OrgID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"` + Events []*OrganizationEvent `gorm:"foreignKey:OrgID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"` } type EnterpriseEvent struct { @@ -233,7 +233,7 @@ type Enterprise struct { EndpointName *string `gorm:"index:idx_ent_name_nocase,collate:nocase"` Endpoint GithubEndpoint `gorm:"foreignKey:EndpointName;constraint:OnDelete:SET NULL"` - Events []EnterpriseEvent `gorm:"foreignKey:EnterpriseID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"` + Events []*EnterpriseEvent `gorm:"foreignKey:EnterpriseID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"` } type Address struct { diff --git a/workers/provider/provider.go b/workers/provider/provider.go index ffc5183d..b1ab1220 100644 --- a/workers/provider/provider.go +++ b/workers/provider/provider.go @@ -6,8 +6,6 @@ import ( "log/slog" "sync" - "golang.org/x/sync/errgroup" - commonParams "github.com/cloudbase/garm-provider-common/params" "github.com/cloudbase/garm/auth" dbCommon "github.com/cloudbase/garm/database/common" @@ -133,24 +131,12 @@ func (p *Provider) Start() error { return nil } - g, _ := errgroup.WithContext(p.ctx) + if err := p.loadAllScaleSets(); err != nil { + return fmt.Errorf("loading all scale sets: %w", err) + } - g.Go(func() error { - if err := p.loadAllScaleSets(); err != nil { - return fmt.Errorf("loading all scale sets: %w", err) - } - return nil - }) - - g.Go(func() error { - if err := p.loadAllRunners(); err != nil { - return fmt.Errorf("loading all runners: %w", err) - } - return nil - }) - - if err := p.waitForErrorGroupOrContextCancelled(g); err != nil { - return fmt.Errorf("waiting for error group: %w", err) + if err := p.loadAllRunners(); err != nil { + return fmt.Errorf("loading all runners: %w", err) } consumer, err := watcher.RegisterConsumer( diff --git a/workers/provider/util.go b/workers/provider/util.go index ca2626c0..8cd33525 100644 --- a/workers/provider/util.go +++ b/workers/provider/util.go @@ -1,8 +1,6 @@ package provider import ( - "golang.org/x/sync/errgroup" - dbCommon "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/database/watcher" ) @@ -13,24 +11,3 @@ func composeProviderWatcher() dbCommon.PayloadFilterFunc { watcher.WithEntityTypeFilter(dbCommon.ScaleSetEntityType), ) } - -func (p *Provider) waitForErrorGroupOrContextCancelled(g *errgroup.Group) error { - if g == nil { - return nil - } - - done := make(chan error, 1) - go func() { - waitErr := g.Wait() - done <- waitErr - }() - - select { - case err := <-done: - return err - case <-p.ctx.Done(): - return p.ctx.Err() - case <-p.quit: - return nil - } -} From f66b651b594808fd4b070d5f6088676c68665c69 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 14 May 2025 21:09:02 +0000 Subject: [PATCH 07/21] Fix findEndpointForJob Signed-off-by: Gabriel Adrian Samfira --- apiserver/controllers/controllers.go | 9 +- cache/cache_test.go | 12 +- cache/tools_cache.go | 40 ++++++- params/params.go | 19 ++-- runner/metadata.go | 159 ++++++++++++++++++--------- runner/pool/pool.go | 31 ++++-- runner/pool/util.go | 21 +++- runner/runner.go | 42 ++++--- util/github/client.go | 5 + workers/cache/gitea_tools.go | 47 ++++++-- workers/cache/tool_cache.go | 2 +- workers/provider/instance_manager.go | 6 +- workers/scaleset/scaleset.go | 7 +- 13 files changed, 287 insertions(+), 113 deletions(-) diff --git a/apiserver/controllers/controllers.go b/apiserver/controllers/controllers.go index 3e6413e0..32da79b3 100644 --- a/apiserver/controllers/controllers.go +++ b/apiserver/controllers/controllers.go @@ -107,8 +107,15 @@ func (a *APIController) handleWorkflowJobEvent(ctx context.Context, w http.Respo signature := r.Header.Get("X-Hub-Signature-256") hookType := r.Header.Get("X-Github-Hook-Installation-Target-Type") + giteaTargetType := r.Header.Get("X-Gitea-Hook-Installation-Target-Type") - if err := a.r.DispatchWorkflowJob(hookType, signature, body); err != nil { + forgeType := runnerParams.GithubEndpointType + if giteaTargetType != "" { + forgeType = runnerParams.GiteaEndpointType + hookType = giteaTargetType + } + + if err := a.r.DispatchWorkflowJob(hookType, signature, forgeType, body); err != nil { switch { case errors.Is(err, gErrors.ErrNotFound): metrics.WebhooksReceived.WithLabelValues( diff --git a/cache/cache_test.go b/cache/cache_test.go index 3e7ed559..2ad63420 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -55,8 +55,8 @@ func (c *CacheTestSuite) TestSetCacheWorks() { c.Require().Len(githubToolsCache.entities, 0) SetGithubToolsCache(c.entity, tools) c.Require().Len(githubToolsCache.entities, 1) - cachedTools, ok := GetGithubToolsCache(c.entity.ID) - c.Require().True(ok) + cachedTools, err := GetGithubToolsCache(c.entity.ID) + c.Require().NoError(err) c.Require().Len(cachedTools, 1) c.Require().Equal(tools[0].GetDownloadURL(), cachedTools[0].GetDownloadURL()) } @@ -76,16 +76,16 @@ func (c *CacheTestSuite) TestTimedOutToolsCache() { entity.updatedAt = entity.updatedAt.Add(-2 * time.Hour) githubToolsCache.entities[c.entity.ID] = entity - cachedTools, ok := GetGithubToolsCache(c.entity.ID) - c.Require().False(ok) + cachedTools, err := GetGithubToolsCache(c.entity.ID) + c.Require().NoError(err) c.Require().Nil(cachedTools) } func (c *CacheTestSuite) TestGetInexistentCache() { c.Require().NotNil(githubToolsCache) c.Require().Len(githubToolsCache.entities, 0) - cachedTools, ok := GetGithubToolsCache(c.entity.ID) - c.Require().False(ok) + cachedTools, err := GetGithubToolsCache(c.entity.ID) + c.Require().NoError(err) c.Require().Nil(cachedTools) } diff --git a/cache/tools_cache.go b/cache/tools_cache.go index 0698c41e..98b58b19 100644 --- a/cache/tools_cache.go +++ b/cache/tools_cache.go @@ -1,6 +1,7 @@ package cache import ( + "fmt" "sync" "time" @@ -20,17 +21,25 @@ func init() { type GithubEntityTools struct { updatedAt time.Time expiresAt time.Time + err error entity params.ForgeEntity tools []commonParams.RunnerApplicationDownload } +func (g GithubEntityTools) Error() string { + if g.err != nil { + return g.err.Error() + } + return "" +} + type GithubToolsCache struct { mux sync.Mutex // entity IDs are UUID4s. It is highly unlikely they will collide (🤞). entities map[string]GithubEntityTools } -func (g *GithubToolsCache) Get(entityID string) ([]commonParams.RunnerApplicationDownload, bool) { +func (g *GithubToolsCache) Get(entityID string) ([]commonParams.RunnerApplicationDownload, error) { g.mux.Lock() defer g.mux.Unlock() @@ -39,12 +48,12 @@ func (g *GithubToolsCache) Get(entityID string) ([]commonParams.RunnerApplicatio if time.Now().UTC().After(cache.expiresAt.Add(-5 * time.Minute)) { // Stale cache, remove it. delete(g.entities, entityID) - return nil, false + return nil, fmt.Errorf("cache expired for entity %s", entityID) } } - return cache.tools, true + return cache.tools, cache.err } - return nil, false + return nil, fmt.Errorf("no cache found for entity %s", entityID) } func (g *GithubToolsCache) Set(entity params.ForgeEntity, tools []commonParams.RunnerApplicationDownload) { @@ -55,6 +64,7 @@ func (g *GithubToolsCache) Set(entity params.ForgeEntity, tools []commonParams.R updatedAt: time.Now(), entity: entity, tools: tools, + err: nil, } if entity.Credentials.ForgeType == params.GithubEndpointType { @@ -64,10 +74,30 @@ func (g *GithubToolsCache) Set(entity params.ForgeEntity, tools []commonParams.R g.entities[entity.ID] = forgeTools } +func (g *GithubToolsCache) SetToolsError(entity params.ForgeEntity, err error) { + g.mux.Lock() + defer g.mux.Unlock() + + // If the entity is not in the cache, add it with the error. + cache, ok := g.entities[entity.ID] + if !ok { + g.entities[entity.ID] = GithubEntityTools{ + updatedAt: time.Now(), + entity: entity, + err: err, + } + return + } + + // Update the error for the existing entity. + cache.err = err + g.entities[entity.ID] = cache +} + func SetGithubToolsCache(entity params.ForgeEntity, tools []commonParams.RunnerApplicationDownload) { githubToolsCache.Set(entity, tools) } -func GetGithubToolsCache(entityID string) ([]commonParams.RunnerApplicationDownload, bool) { +func GetGithubToolsCache(entityID string) ([]commonParams.RunnerApplicationDownload, error) { return githubToolsCache.Get(entityID) } diff --git a/params/params.go b/params/params.go index 73afa0f4..052b2c8b 100644 --- a/params/params.go +++ b/params/params.go @@ -1099,13 +1099,18 @@ func (g ForgeEntity) GetCreatedAt() time.Time { } func (g ForgeEntity) ForgeURL() string { - switch g.EntityType { - case ForgeEntityTypeRepository: - return fmt.Sprintf("%s/%s/%s", g.Credentials.BaseURL, g.Owner, g.Name) - case ForgeEntityTypeOrganization: - return fmt.Sprintf("%s/%s", g.Credentials.BaseURL, g.Owner) - case ForgeEntityTypeEnterprise: - return fmt.Sprintf("%s/enterprises/%s", g.Credentials.BaseURL, g.Owner) + switch g.Credentials.ForgeType { + case GiteaEndpointType: + return g.Credentials.Endpoint.APIBaseURL + default: + switch g.EntityType { + case ForgeEntityTypeRepository: + return fmt.Sprintf("%s/%s/%s", g.Credentials.BaseURL, g.Owner, g.Name) + case ForgeEntityTypeOrganization: + return fmt.Sprintf("%s/%s", g.Credentials.BaseURL, g.Owner) + case ForgeEntityTypeEnterprise: + return fmt.Sprintf("%s/enterprises/%s", g.Credentials.BaseURL, g.Owner) + } } return "" } diff --git a/runner/metadata.go b/runner/metadata.go index 8a9c8469..3df7966a 100644 --- a/runner/metadata.go +++ b/runner/metadata.go @@ -16,7 +16,7 @@ import ( "github.com/cloudbase/garm/params" ) -var systemdUnitTemplate = `[Unit] +var githubSystemdUnitTemplate = `[Unit] Description=GitHub Actions Runner ({{.ServiceName}}) After=network.target @@ -32,11 +32,24 @@ TimeoutStopSec=5min WantedBy=multi-user.target ` -func validateInstanceState(ctx context.Context) (params.Instance, error) { - if !auth.InstanceHasJITConfig(ctx) { - return params.Instance{}, fmt.Errorf("instance not configured for JIT: %w", runnerErrors.ErrNotFound) - } +var giteaSystemdUnitTemplate = `[Unit] +Description=Act Runner ({{.ServiceName}}) +After=network.target +[Service] +ExecStart=/home/{{.RunAsUser}}/act-runner/act_runner daemon --once +User={{.RunAsUser}} +WorkingDirectory=/home/{{.RunAsUser}}/act-runner +KillMode=process +KillSignal=SIGTERM +TimeoutStopSec=5min +Restart=always + +[Install] +WantedBy=multi-user.target +` + +func validateInstanceState(ctx context.Context) (params.Instance, error) { status := auth.InstanceRunnerStatus(ctx) if status != params.RunnerPending && status != params.RunnerInstalling { return params.Instance{}, runnerErrors.ErrUnauthorized @@ -49,6 +62,56 @@ func validateInstanceState(ctx context.Context) (params.Instance, error) { return instance, nil } +func (r *Runner) getForgeEntityFromInstance(ctx context.Context, instance params.Instance) (params.ForgeEntity, error) { + var entityGetter params.EntityGetter + var err error + switch { + case instance.PoolID != "": + entityGetter, err = r.store.GetPoolByID(r.ctx, instance.PoolID) + case instance.ScaleSetID != 0: + entityGetter, err = r.store.GetScaleSetByID(r.ctx, instance.ScaleSetID) + default: + return params.ForgeEntity{}, errors.New("instance not associated with a pool or scale set") + } + + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext( + ctx, "failed to get entity getter", + "instance", instance.Name) + return params.ForgeEntity{}, errors.Wrap(err, "fetching entity getter") + } + + poolEntity, err := entityGetter.GetEntity() + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext( + ctx, "failed to get entity", + "instance", instance.Name) + return params.ForgeEntity{}, errors.Wrap(err, "fetching entity") + } + + entity, err := r.store.GetForgeEntity(r.ctx, poolEntity.EntityType, poolEntity.ID) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext( + ctx, "failed to get entity", + "instance", instance.Name) + return params.ForgeEntity{}, errors.Wrap(err, "fetching entity") + } + return entity, nil +} + +func (r *Runner) getServiceNameForEntity(entity params.ForgeEntity) (string, error) { + switch entity.EntityType { + case params.ForgeEntityTypeEnterprise: + return fmt.Sprintf("actions.runner.%s.%s", entity.Owner, entity.Name), nil + case params.ForgeEntityTypeOrganization: + return fmt.Sprintf("actions.runner.%s.%s", entity.Owner, entity.Name), nil + case params.ForgeEntityTypeRepository: + return fmt.Sprintf("actions.runner.%s-%s.%s", entity.Owner, entity.Name, entity.Name), nil + default: + return "", errors.New("unknown entity type") + } +} + func (r *Runner) GetRunnerServiceName(ctx context.Context) (string, error) { instance, err := validateInstanceState(ctx) if err != nil { @@ -56,64 +119,51 @@ func (r *Runner) GetRunnerServiceName(ctx context.Context) (string, error) { ctx, "failed to get instance params") return "", runnerErrors.ErrUnauthorized } - var entity params.ForgeEntity - - switch { - case instance.PoolID != "": - pool, err := r.store.GetPoolByID(r.ctx, instance.PoolID) - if err != nil { - slog.With(slog.Any("error", err)).ErrorContext( - ctx, "failed to get pool", - "pool_id", instance.PoolID) - return "", errors.Wrap(err, "fetching pool") - } - entity, err = pool.GetEntity() - if err != nil { - slog.With(slog.Any("error", err)).ErrorContext( - ctx, "failed to get pool entity", - "pool_id", instance.PoolID) - return "", errors.Wrap(err, "fetching pool entity") - } - case instance.ScaleSetID != 0: - scaleSet, err := r.store.GetScaleSetByID(r.ctx, instance.ScaleSetID) - if err != nil { - slog.With(slog.Any("error", err)).ErrorContext( - ctx, "failed to get scale set", - "scale_set_id", instance.ScaleSetID) - return "", errors.Wrap(err, "fetching scale set") - } - entity, err = scaleSet.GetEntity() - if err != nil { - slog.With(slog.Any("error", err)).ErrorContext( - ctx, "failed to get scale set entity", - "scale_set_id", instance.ScaleSetID) - return "", errors.Wrap(err, "fetching scale set entity") - } - default: - return "", errors.New("instance not associated with a pool or scale set") + entity, err := r.getForgeEntityFromInstance(ctx, instance) + if err != nil { + slog.ErrorContext(r.ctx, "failed to get entity", "error", err) + return "", errors.Wrap(err, "fetching entity") } - tpl := "actions.runner.%s.%s" - var serviceName string - switch entity.EntityType { - case params.ForgeEntityTypeEnterprise: - serviceName = fmt.Sprintf(tpl, entity.Owner, instance.Name) - case params.ForgeEntityTypeOrganization: - serviceName = fmt.Sprintf(tpl, entity.Owner, instance.Name) - case params.ForgeEntityTypeRepository: - serviceName = fmt.Sprintf(tpl, fmt.Sprintf("%s-%s", entity.Owner, entity.Name), instance.Name) + serviceName, err := r.getServiceNameForEntity(entity) + if err != nil { + slog.ErrorContext(r.ctx, "failed to get service name", "error", err) + return "", errors.Wrap(err, "fetching service name") } return serviceName, nil } func (r *Runner) GenerateSystemdUnitFile(ctx context.Context, runAsUser string) ([]byte, error) { - serviceName, err := r.GetRunnerServiceName(ctx) + instance, err := validateInstanceState(ctx) if err != nil { - return nil, errors.Wrap(err, "fetching runner service name") + slog.With(slog.Any("error", err)).ErrorContext( + ctx, "failed to get instance params") + return nil, runnerErrors.ErrUnauthorized + } + entity, err := r.getForgeEntityFromInstance(ctx, instance) + if err != nil { + slog.ErrorContext(r.ctx, "failed to get entity", "error", err) + return nil, errors.Wrap(err, "fetching entity") } - unitTemplate, err := template.New("").Parse(systemdUnitTemplate) + serviceName, err := r.getServiceNameForEntity(entity) if err != nil { + slog.ErrorContext(r.ctx, "failed to get service name", "error", err) + return nil, errors.Wrap(err, "fetching service name") + } + + var unitTemplate *template.Template + switch entity.Credentials.ForgeType { + case params.GithubEndpointType: + unitTemplate, err = template.New("").Parse(githubSystemdUnitTemplate) + case params.GiteaEndpointType: + unitTemplate, err = template.New("").Parse(giteaSystemdUnitTemplate) + default: + slog.ErrorContext(r.ctx, "unknown forge type", "forge_type", entity.Credentials.ForgeType) + return nil, errors.New("unknown forge type") + } + if err != nil { + slog.ErrorContext(r.ctx, "failed to parse template", "error", err) return nil, errors.Wrap(err, "parsing template") } @@ -131,12 +181,17 @@ func (r *Runner) GenerateSystemdUnitFile(ctx context.Context, runAsUser string) var unitFile bytes.Buffer if err := unitTemplate.Execute(&unitFile, data); err != nil { + slog.ErrorContext(r.ctx, "failed to execute template", "error", err) return nil, errors.Wrap(err, "executing template") } return unitFile.Bytes(), nil } func (r *Runner) GetJITConfigFile(ctx context.Context, file string) ([]byte, error) { + if !auth.InstanceHasJITConfig(ctx) { + return nil, fmt.Errorf("instance not configured for JIT: %w", runnerErrors.ErrNotFound) + } + instance, err := validateInstanceState(ctx) if err != nil { slog.With(slog.Any("error", err)).ErrorContext( diff --git a/runner/pool/pool.go b/runner/pool/pool.go index 68de0ec3..8b02b593 100644 --- a/runner/pool/pool.go +++ b/runner/pool/pool.go @@ -47,15 +47,15 @@ import ( ) var ( - poolIDLabelprefix = "runner-pool-id:" - controllerLabelPrefix = "runner-controller-id:" + poolIDLabelprefix = "runner-pool-id" + controllerLabelPrefix = "runner-controller-id" // We tag runners that have been spawned as a result of a queued job with the job ID // that spawned them. There is no way to guarantee that the runner spawned in response to a particular // job, will be picked up by that job. We mark them so as in the very likely event that the runner // has picked up a different job, we can clear the lock on the job that spaned it. // The job it picked up would already be transitioned to in_progress so it will be ignored by the // consume loop. - jobLabelPrefix = "in_response_to_job:" + jobLabelPrefix = "in_response_to_job" ) const ( @@ -296,7 +296,8 @@ func (r *basePoolManager) HandleWorkflowJob(job params.WorkflowJob) error { func jobIDFromLabels(labels []string) int64 { for _, lbl := range labels { if strings.HasPrefix(lbl, jobLabelPrefix) { - jobID, err := strconv.ParseInt(lbl[len(jobLabelPrefix):], 10, 64) + trimLength := min(len(jobLabelPrefix)+1, len(lbl)) + jobID, err := strconv.ParseInt(lbl[trimLength:], 10, 64) if err != nil { return 0 } @@ -361,21 +362,21 @@ func (r *basePoolManager) startLoopForFunction(f func() error, interval time.Dur } func (r *basePoolManager) updateTools() error { - // Update tools cache. - tools, err := r.FetchTools() + tools, err := cache.GetGithubToolsCache(r.entity.ID) if err != nil { slog.With(slog.Any("error", err)).ErrorContext( r.ctx, "failed to update tools for entity", "entity", r.entity.String()) r.SetPoolRunningState(false, err.Error()) return fmt.Errorf("failed to update tools for entity %s: %w", r.entity.String(), err) } + r.mux.Lock() r.tools = tools r.mux.Unlock() slog.DebugContext(r.ctx, "successfully updated tools") r.SetPoolRunningState(true, "") - return err + return nil } // cleanupOrphanedProviderRunners compares runners in github with local runners and removes @@ -995,11 +996,11 @@ func (r *basePoolManager) paramsWorkflowJobToParamsJob(job params.WorkflowJob) ( } func (r *basePoolManager) poolLabel(poolID string) string { - return fmt.Sprintf("%s%s", poolIDLabelprefix, poolID) + return fmt.Sprintf("%s=%s", poolIDLabelprefix, poolID) } func (r *basePoolManager) controllerLabel() string { - return fmt.Sprintf("%s%s", controllerLabelPrefix, r.controllerInfo.ControllerID.String()) + return fmt.Sprintf("%s=%s", controllerLabelPrefix, r.controllerInfo.ControllerID.String()) } func (r *basePoolManager) updateArgsFromProviderInstance(providerInstance commonParams.ProviderInstance) params.UpdateInstanceParams { @@ -1613,6 +1614,16 @@ func (r *basePoolManager) Start() error { initialToolUpdate := make(chan struct{}, 1) go func() { slog.Info("running initial tool update") + for { + slog.DebugContext(r.ctx, "waiting for tools to be available") + hasTools, stopped := r.waitForToolsOrCancel() + if stopped { + return + } + if hasTools { + break + } + } if err := r.updateTools(); err != nil { slog.With(slog.Any("error", err)).Error("failed to update tools") } @@ -1804,7 +1815,7 @@ func (r *basePoolManager) consumeQueuedJobs() error { } jobLabels := []string{ - fmt.Sprintf("%s%d", jobLabelPrefix, job.ID), + fmt.Sprintf("%s=%d", jobLabelPrefix, job.ID), } for i := 0; i < poolRR.Len(); i++ { pool, err := poolRR.Next() diff --git a/runner/pool/util.go b/runner/pool/util.go index 25fdc73f..4c4bf5b1 100644 --- a/runner/pool/util.go +++ b/runner/pool/util.go @@ -5,11 +5,13 @@ import ( "strings" "sync" "sync/atomic" + "time" "github.com/google/go-github/v71/github" runnerErrors "github.com/cloudbase/garm-provider-common/errors" commonParams "github.com/cloudbase/garm-provider-common/params" + "github.com/cloudbase/garm/cache" dbCommon "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/database/watcher" "github.com/cloudbase/garm/params" @@ -91,7 +93,8 @@ func instanceInList(instanceName string, instances []commonParams.ProviderInstan func controllerIDFromLabels(labels []string) string { for _, lbl := range labels { if strings.HasPrefix(lbl, controllerLabelPrefix) { - return lbl[len(controllerLabelPrefix):] + trimLength := min(len(controllerLabelPrefix)+1, len(lbl)) + return lbl[trimLength:] } } return "" @@ -134,3 +137,19 @@ func composeWatcherFilters(entity params.ForgeEntity) dbCommon.PayloadFilterFunc watcher.WithForgeCredentialsFilter(entity.Credentials), ) } + +func (r *basePoolManager) waitForToolsOrCancel() (hasTools, stopped bool) { + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + select { + case <-ticker.C: + if _, err := cache.GetGithubToolsCache(r.entity.ID); err != nil { + return false, false + } + return true, false + case <-r.quit: + return false, true + case <-r.ctx.Done(): + return false, true + } +} diff --git a/runner/runner.go b/runner/runner.go index 6d5bc5eb..e02ee698 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -602,7 +602,7 @@ func (r *Runner) validateHookBody(signature, secret string, body []byte) error { return nil } -func (r *Runner) findEndpointForJob(job params.WorkflowJob) (params.ForgeEndpoint, error) { +func (r *Runner) findEndpointForJob(job params.WorkflowJob, forgeType params.EndpointType) (params.ForgeEndpoint, error) { uri, err := url.ParseRequestURI(job.WorkflowJob.HTMLURL) if err != nil { return params.ForgeEndpoint{}, errors.Wrap(err, "parsing job URL") @@ -614,12 +614,23 @@ func (r *Runner) findEndpointForJob(job params.WorkflowJob) (params.ForgeEndpoin // a GHES involved, those users will have just one extra endpoint or 2 (if they also have a // test env). But there should be a relatively small number, regardless. So we don't really care // that much about the performance of this function. - endpoints, err := r.store.ListGithubEndpoints(r.ctx) + var endpoints []params.ForgeEndpoint + switch forgeType { + case params.GithubEndpointType: + endpoints, err = r.store.ListGithubEndpoints(r.ctx) + case params.GiteaEndpointType: + endpoints, err = r.store.ListGiteaEndpoints(r.ctx) + default: + return params.ForgeEndpoint{}, runnerErrors.NewBadRequestError("unknown forge type %s", forgeType) + } + if err != nil { return params.ForgeEndpoint{}, errors.Wrap(err, "fetching github endpoints") } for _, ep := range endpoints { - if ep.BaseURL == baseURI { + slog.DebugContext(r.ctx, "checking endpoint", "base_uri", baseURI, "endpoint", ep.BaseURL) + epBaseURI := strings.TrimSuffix(ep.BaseURL, "/") + if epBaseURI == baseURI { return ep, nil } } @@ -627,18 +638,21 @@ func (r *Runner) findEndpointForJob(job params.WorkflowJob) (params.ForgeEndpoin return params.ForgeEndpoint{}, runnerErrors.NewNotFoundError("no endpoint found for job") } -func (r *Runner) DispatchWorkflowJob(hookTargetType, signature string, jobData []byte) error { +func (r *Runner) DispatchWorkflowJob(hookTargetType, signature string, forgeType params.EndpointType, jobData []byte) error { if len(jobData) == 0 { + slog.ErrorContext(r.ctx, "missing job data") return runnerErrors.NewBadRequestError("missing job data") } var job params.WorkflowJob if err := json.Unmarshal(jobData, &job); err != nil { + slog.ErrorContext(r.ctx, "failed to unmarshal job data", "error", err) return errors.Wrapf(runnerErrors.ErrBadRequest, "invalid job data: %s", err) } - endpoint, err := r.findEndpointForJob(job) + endpoint, err := r.findEndpointForJob(job, forgeType) if err != nil { + slog.ErrorContext(r.ctx, "failed to find endpoint for job", "error", err) return errors.Wrap(err, "finding endpoint for job") } @@ -867,15 +881,17 @@ func (r *Runner) DeleteRunner(ctx context.Context, instanceName string, forceDel } if err != nil { - if errors.Is(err, runnerErrors.ErrUnauthorized) && instance.PoolID != "" { - poolMgr, err := r.getPoolManagerFromInstance(ctx, instance) - if err != nil { - return errors.Wrap(err, "fetching pool manager for instance") + if !errors.Is(err, runnerErrors.ErrNotFound) { + if errors.Is(err, runnerErrors.ErrUnauthorized) && instance.PoolID != "" { + poolMgr, err := r.getPoolManagerFromInstance(ctx, instance) + if err != nil { + return errors.Wrap(err, "fetching pool manager for instance") + } + poolMgr.SetPoolRunningState(false, fmt.Sprintf("failed to remove runner: %q", err)) + } + if !bypassGithubUnauthorized { + return errors.Wrap(err, "removing runner from github") } - poolMgr.SetPoolRunningState(false, fmt.Sprintf("failed to remove runner: %q", err)) - } - if !bypassGithubUnauthorized { - return errors.Wrap(err, "removing runner from github") } } } diff --git a/util/github/client.go b/util/github/client.go index bcdebc13..a46e4ab7 100644 --- a/util/github/client.go +++ b/util/github/client.go @@ -229,6 +229,7 @@ func (g *githubClient) ListEntityRunnerApplicationDownloads(ctx context.Context) } func parseError(response *github.Response, err error) error { + slog.Debug("parsing error", "status_code", response.StatusCode, "response", response, "error", err) switch response.StatusCode { case http.StatusNotFound: return runnerErrors.ErrNotFound @@ -251,6 +252,10 @@ func parseError(response *github.Response, err error) error { case http.StatusUnprocessableEntity: return runnerErrors.ErrBadRequest default: + // ugly hack. Gitea returns 500 if we try to remove a runner that does not exist. + if strings.Contains(err.Error(), "does not exist") { + return runnerErrors.ErrNotFound + } return err } } diff --git a/workers/cache/gitea_tools.go b/workers/cache/gitea_tools.go index 8b2fc758..9d6b2307 100644 --- a/workers/cache/gitea_tools.go +++ b/workers/cache/gitea_tools.go @@ -20,6 +20,18 @@ const ( GiteaRunnerMinimumVersion = "v0.2.12" ) +var ( + githubArchMapping map[string]string = map[string]string{ + "x86_64": "x64", + "amd64": "x64", + "armv7l": "arm", + "aarch64": "arm64", + "x64": "x64", + "arm": "arm", + "arm64": "arm64", + } +) + var nightlyActRunner = GiteaEntityTool{ TagName: "nightly", Name: "nightly", @@ -50,36 +62,39 @@ type GiteaToolsAssets struct { DownloadURL string `json:"browser_download_url"` } -func (g GiteaToolsAssets) GetOS() *string { +func (g GiteaToolsAssets) GetOS() (*string, error) { if g.Name == "" { - return nil + return nil, fmt.Errorf("gitea tools name is empty") } parts := strings.SplitN(g.Name, "-", 4) if len(parts) != 4 { - return nil + return nil, fmt.Errorf("could not parse asset name") } os := parts[2] - return &os + return &os, nil } -func (g GiteaToolsAssets) GetArch() *string { +func (g GiteaToolsAssets) GetArch() (*string, error) { if g.Name == "" { - return nil + return nil, fmt.Errorf("gitea tools name is empty") } parts := strings.SplitN(g.Name, "-", 4) if len(parts) != 4 { - return nil + return nil, fmt.Errorf("could not parse asset name") } archParts := strings.SplitN(parts[3], ".", 2) if len(archParts) == 0 { - return nil + return nil, fmt.Errorf("unexpected asset name format") } - arch := archParts[0] - return &arch + arch := githubArchMapping[archParts[0]] + if arch == "" { + return nil, fmt.Errorf("could not find arch for %s", archParts[0]) + } + return &arch, nil } type GiteaEntityTool struct { @@ -140,9 +155,17 @@ func getTools() ([]commonParams.RunnerApplicationDownload, error) { ret := []commonParams.RunnerApplicationDownload{} for _, asset := range latest.Assets { + arch, err := asset.GetArch() + if err != nil { + return nil, fmt.Errorf("getting arch: %w", err) + } + os, err := asset.GetOS() + if err != nil { + return nil, fmt.Errorf("getting os: %w", err) + } ret = append(ret, commonParams.RunnerApplicationDownload{ - OS: asset.GetOS(), - Architecture: asset.GetArch(), + OS: os, + Architecture: arch, DownloadURL: &asset.DownloadURL, Filename: &asset.Name, }) diff --git a/workers/cache/tool_cache.go b/workers/cache/tool_cache.go index d3c74673..941131d7 100644 --- a/workers/cache/tool_cache.go +++ b/workers/cache/tool_cache.go @@ -49,7 +49,7 @@ func (t *toolsUpdater) Start() error { t.running = true t.quit = make(chan struct{}) - slog.DebugContext(t.ctx, "starting tools updater", "entity", t.entity.String(), "forge_type", t.entity.Credentials) + slog.DebugContext(t.ctx, "starting tools updater", "entity", t.entity.String(), "forge_type", t.entity.Credentials.ForgeType) switch t.entity.Credentials.ForgeType { case params.GithubEndpointType: diff --git a/workers/provider/instance_manager.go b/workers/provider/instance_manager.go index 47e875a0..d0e61b72 100644 --- a/workers/provider/instance_manager.go +++ b/workers/provider/instance_manager.go @@ -148,9 +148,9 @@ func (i *instanceManager) handleCreateInstanceInProvider(instance params.Instanc if err != nil { return fmt.Errorf("creating instance token: %w", err) } - tools, ok := cache.GetGithubToolsCache(entity.ID) - if !ok { - return fmt.Errorf("tools not found in cache for entity %s", entity.String()) + tools, err := cache.GetGithubToolsCache(entity.ID) + if err != nil { + return fmt.Errorf("tools not found in cache for entity %s: %w", entity.String(), err) } bootstrapArgs := commonParams.BootstrapInstance{ diff --git a/workers/scaleset/scaleset.go b/workers/scaleset/scaleset.go index 1090388d..b3bfe332 100644 --- a/workers/scaleset/scaleset.go +++ b/workers/scaleset/scaleset.go @@ -776,8 +776,11 @@ func (w *Worker) waitForToolsOrCancel() (hasTools, stopped bool) { if err != nil { slog.ErrorContext(w.ctx, "error getting entity", "error", err) } - _, ok := cache.GetGithubToolsCache(entity.ID) - return ok, false + if _, err := cache.GetGithubToolsCache(entity.ID); err != nil { + slog.DebugContext(w.ctx, "tools not found in cache; waiting for tools") + return false, false + } + return true, false case <-w.quit: return false, true case <-w.ctx.Done(): From 3fe4cef8849444b54c0dfce90480bc10ead869ce Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 14 May 2025 21:14:31 +0000 Subject: [PATCH 08/21] Cleanup unused code Signed-off-by: Gabriel Adrian Samfira --- workers/pools/controller.go | 1 - 1 file changed, 1 deletion(-) delete mode 100644 workers/pools/controller.go diff --git a/workers/pools/controller.go b/workers/pools/controller.go deleted file mode 100644 index 058ebec6..00000000 --- a/workers/pools/controller.go +++ /dev/null @@ -1 +0,0 @@ -package pools From 39ac658527c67b60161873e729870527d8b550e5 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 14 May 2025 21:33:46 +0000 Subject: [PATCH 09/21] Add forge type to repo list Signed-off-by: Gabriel Adrian Samfira --- cmd/garm-cli/cmd/repository.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/garm-cli/cmd/repository.go b/cmd/garm-cli/cmd/repository.go index b24cf039..c94495cd 100644 --- a/cmd/garm-cli/cmd/repository.go +++ b/cmd/garm-cli/cmd/repository.go @@ -357,13 +357,17 @@ func formatRepositories(repos []params.Repository) { return } t := table.NewWriter() - header := table.Row{"ID", "Owner", "Name", "Endpoint", "Credentials name", "Pool Balancer Type", "Pool mgr running"} + header := table.Row{"ID", "Owner", "Name", "Endpoint", "Credentials name", "Pool Balancer Type", "Forge type", "Pool mgr running"} if long { header = append(header, "Created At", "Updated At") } t.AppendHeader(header) for _, val := range repos { - row := table.Row{val.ID, val.Owner, val.Name, val.Endpoint.Name, val.GetCredentialsName(), val.GetBalancerType(), val.PoolManagerStatus.IsRunning} + forgeType := val.Endpoint.EndpointType + if forgeType == "" { + forgeType = params.GithubEndpointType + } + row := table.Row{val.ID, val.Owner, val.Name, val.Endpoint.Name, val.GetCredentialsName(), val.GetBalancerType(), forgeType, val.PoolManagerStatus.IsRunning} if long { row = append(row, val.CreatedAt, val.UpdatedAt) } From 0270117e8d0d3ced40e47e748c6498fb02c8e163 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 14 May 2025 22:01:11 +0000 Subject: [PATCH 10/21] Fix lint errors Signed-off-by: Gabriel Adrian Samfira --- cache/credentials_cache.go | 6 ++++-- database/sql/gitea.go | 5 +++-- workers/cache/gitea_tools.go | 23 +++++++++++------------ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/cache/credentials_cache.go b/cache/credentials_cache.go index d5626f40..6dbef775 100644 --- a/cache/credentials_cache.go +++ b/cache/credentials_cache.go @@ -6,8 +6,10 @@ import ( "github.com/cloudbase/garm/params" ) -var credentialsCache *CredentialCache -var giteaCredentialsCache *CredentialCache +var ( + credentialsCache *CredentialCache + giteaCredentialsCache *CredentialCache +) func init() { ghCredentialsCache := &CredentialCache{ diff --git a/database/sql/gitea.go b/database/sql/gitea.go index 5ce46663..8d89b82d 100644 --- a/database/sql/gitea.go +++ b/database/sql/gitea.go @@ -4,12 +4,13 @@ import ( "context" "log/slog" + "github.com/pkg/errors" + "gorm.io/gorm" + runnerErrors "github.com/cloudbase/garm-provider-common/errors" "github.com/cloudbase/garm/auth" "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/params" - "github.com/pkg/errors" - "gorm.io/gorm" ) func (s *sqlDatabase) CreateGiteaEndpoint(_ context.Context, param params.CreateGiteaEndpointParams) (ghEndpoint params.ForgeEndpoint, err error) { diff --git a/workers/cache/gitea_tools.go b/workers/cache/gitea_tools.go index 9d6b2307..4474f7df 100644 --- a/workers/cache/gitea_tools.go +++ b/workers/cache/gitea_tools.go @@ -8,8 +8,9 @@ import ( "strings" "time" - commonParams "github.com/cloudbase/garm-provider-common/params" "golang.org/x/mod/semver" + + commonParams "github.com/cloudbase/garm-provider-common/params" ) const ( @@ -20,17 +21,15 @@ const ( GiteaRunnerMinimumVersion = "v0.2.12" ) -var ( - githubArchMapping map[string]string = map[string]string{ - "x86_64": "x64", - "amd64": "x64", - "armv7l": "arm", - "aarch64": "arm64", - "x64": "x64", - "arm": "arm", - "arm64": "arm64", - } -) +var githubArchMapping = map[string]string{ + "x86_64": "x64", + "amd64": "x64", + "armv7l": "arm", + "aarch64": "arm64", + "x64": "x64", + "arm": "arm", + "arm64": "arm64", +} var nightlyActRunner = GiteaEntityTool{ TagName: "nightly", From b4e92a69c98b4750f7003a3612f7e02902939776 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 14 May 2025 23:20:18 +0000 Subject: [PATCH 11/21] Fix existing tests Signed-off-by: Gabriel Adrian Samfira --- cache/cache_test.go | 26 ++++++++++++++++++-------- cache/tools_cache.go | 2 +- database/sql/github.go | 11 ++++++++--- database/sql/repositories_test.go | 9 --------- runner/repositories_test.go | 1 + 5 files changed, 28 insertions(+), 21 deletions(-) diff --git a/cache/cache_test.go b/cache/cache_test.go index 2ad63420..0f514329 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -22,6 +22,11 @@ func (c *CacheTestSuite) SetupTest() { EntityType: params.ForgeEntityTypeOrganization, Name: "test", Owner: "test", + Credentials: params.ForgeCredentials{ + ID: 1, + Name: "test", + ForgeType: params.GithubEndpointType, + }, } } @@ -50,7 +55,6 @@ func (c *CacheTestSuite) TestSetCacheWorks() { DownloadURL: garmTesting.Ptr("https://example.com"), }, } - c.Require().NotNil(githubToolsCache) c.Require().Len(githubToolsCache.entities, 0) SetGithubToolsCache(c.entity, tools) @@ -71,13 +75,17 @@ func (c *CacheTestSuite) TestTimedOutToolsCache() { c.Require().NotNil(githubToolsCache) c.Require().Len(githubToolsCache.entities, 0) SetGithubToolsCache(c.entity, tools) - c.Require().Len(githubToolsCache.entities, 1) entity := githubToolsCache.entities[c.entity.ID] - entity.updatedAt = entity.updatedAt.Add(-2 * time.Hour) + + c.Require().Equal(int64(entity.expiresAt.Sub(entity.updatedAt).Minutes()), int64(60)) + c.Require().Len(githubToolsCache.entities, 1) + entity = githubToolsCache.entities[c.entity.ID] + entity.updatedAt = entity.updatedAt.Add(-3 * time.Hour) + entity.expiresAt = entity.updatedAt.Add(-2 * time.Hour) githubToolsCache.entities[c.entity.ID] = entity cachedTools, err := GetGithubToolsCache(c.entity.ID) - c.Require().NoError(err) + c.Require().Error(err) c.Require().Nil(cachedTools) } @@ -85,7 +93,7 @@ func (c *CacheTestSuite) TestGetInexistentCache() { c.Require().NotNil(githubToolsCache) c.Require().Len(githubToolsCache.entities, 0) cachedTools, err := GetGithubToolsCache(c.entity.ID) - c.Require().NoError(err) + c.Require().Error(err) c.Require().Nil(cachedTools) } @@ -280,7 +288,8 @@ func (c *CacheTestSuite) TestReplaceEntityPools() { Name: "test", Owner: "test", Credentials: params.ForgeCredentials{ - ID: 1, + ID: 1, + ForgeType: params.GithubEndpointType, }, } pool1 := params.Pool{ @@ -291,8 +300,9 @@ func (c *CacheTestSuite) TestReplaceEntityPools() { } credentials := params.ForgeCredentials{ - ID: 1, - Name: "test", + ID: 1, + Name: "test", + ForgeType: params.GithubEndpointType, } SetGithubCredentials(credentials) diff --git a/cache/tools_cache.go b/cache/tools_cache.go index 98b58b19..8675bec9 100644 --- a/cache/tools_cache.go +++ b/cache/tools_cache.go @@ -68,7 +68,7 @@ func (g *GithubToolsCache) Set(entity params.ForgeEntity, tools []commonParams.R } if entity.Credentials.ForgeType == params.GithubEndpointType { - forgeTools.expiresAt = time.Now().Add(24 * time.Hour) + forgeTools.expiresAt = time.Now().Add(1 * time.Hour) } g.entities[entity.ID] = forgeTools diff --git a/database/sql/github.go b/database/sql/github.go index d2c05244..08d6bdb3 100644 --- a/database/sql/github.go +++ b/database/sql/github.go @@ -290,8 +290,11 @@ func (s *sqlDatabase) getGithubCredentialsByName(ctx context.Context, tx *gorm.D if detailed { q = q. Preload("Repositories"). + Preload("Repositories.Credentials"). Preload("Organizations"). - Preload("Enterprises") + Preload("Organizations.Credentials"). + Preload("Enterprises"). + Preload("Enterprises.Credentials") } userID, err := getUIDFromContext(ctx) @@ -316,7 +319,6 @@ func (s *sqlDatabase) GetGithubCredentialsByName(ctx context.Context, name strin if err != nil { return params.ForgeCredentials{}, errors.Wrap(err, "fetching github credentials") } - return s.sqlToCommonForgeCredentials(creds) } @@ -327,8 +329,11 @@ func (s *sqlDatabase) GetGithubCredentials(ctx context.Context, id uint, detaile if detailed { q = q. Preload("Repositories"). + Preload("Repositories.Credentials"). Preload("Organizations"). - Preload("Enterprises") + Preload("Organizations.Credentials"). + Preload("Enterprises"). + Preload("Enterprises.Credentials") } if !auth.IsAdmin(ctx) { diff --git a/database/sql/repositories_test.go b/database/sql/repositories_test.go index 73104a2f..0e2fa08f 100644 --- a/database/sql/repositories_test.go +++ b/database/sql/repositories_test.go @@ -249,15 +249,6 @@ func (s *RepoTestSuite) TestCreateRepositoryInvalidDBPassphrase() { func (s *RepoTestSuite) TestCreateRepositoryInvalidDBCreateErr() { s.Fixtures.SQLMock.ExpectBegin() - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE user_id = ? AND name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). - WithArgs(s.adminUserID, s.Fixtures.Repos[0].CredentialsName, 1). - WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). - AddRow(s.testCreds.ID, s.githubEndpoint.Name)) - s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). - WithArgs(s.testCreds.Endpoint.Name). - WillReturnRows(sqlmock.NewRows([]string{"name"}). - AddRow(s.githubEndpoint.Name)) s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `repositories`")). WillReturnError(fmt.Errorf("creating repo mock error")) diff --git a/runner/repositories_test.go b/runner/repositories_test.go index 4e891e4b..0adf40d7 100644 --- a/runner/repositories_test.go +++ b/runner/repositories_test.go @@ -116,6 +116,7 @@ func (s *RepoTestSuite) SetupTest() { Name: "test-repo-create", CredentialsName: s.testCreds.Name, WebhookSecret: "test-create-repo-webhook-secret", + ForgeType: params.GithubEndpointType, }, CreatePoolParams: params.CreatePoolParams{ ProviderName: "test-provider", From 08511e2e7fa5b817a5f9aef7a032b621d1ce8bb0 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Thu, 15 May 2025 09:15:44 +0000 Subject: [PATCH 12/21] Account for gitea credentials in cache and watchers Signed-off-by: Gabriel Adrian Samfira --- apiserver/events/params.go | 2 +- cache/entity_cache.go | 12 ++++++++---- database/watcher/filters.go | 2 +- runner/pool/watcher.go | 2 +- workers/cache/cache.go | 14 +++++++++++--- workers/entity/worker_watcher.go | 4 ++-- 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/apiserver/events/params.go b/apiserver/events/params.go index 274d3f1e..49bf47fa 100644 --- a/apiserver/events/params.go +++ b/apiserver/events/params.go @@ -14,7 +14,7 @@ func (f Filter) Validate() error { case common.RepositoryEntityType, common.OrganizationEntityType, common.EnterpriseEntityType, common.PoolEntityType, common.UserEntityType, common.InstanceEntityType, common.JobEntityType, common.ControllerEntityType, common.GithubCredentialsEntityType, - common.GithubEndpointEntityType: + common.GiteaCredentialsEntityType, common.ScaleSetEntityType, common.GithubEndpointEntityType: default: return common.ErrInvalidEntityType } diff --git a/cache/entity_cache.go b/cache/entity_cache.go index 6bd1f2c7..bbbc385d 100644 --- a/cache/entity_cache.go +++ b/cache/entity_cache.go @@ -231,13 +231,17 @@ func (e *EntityCache) GetEntityScaleSets(entityID string) []params.ScaleSet { return nil } -func (e *EntityCache) GetEntitiesUsingGredentials(credsID uint) []params.ForgeEntity { +func (e *EntityCache) GetEntitiesUsingCredentials(creds params.ForgeCredentials) []params.ForgeEntity { e.mux.Lock() defer e.mux.Unlock() var entities []params.ForgeEntity for _, cache := range e.entities { - if cache.Entity.Credentials.GetID() == credsID { + if cache.Entity.Credentials.ForgeType != creds.ForgeType { + continue + } + + if cache.Entity.Credentials.GetID() == creds.GetID() { entities = append(entities, cache.Entity) } } @@ -357,8 +361,8 @@ func UpdateCredentialsInAffectedEntities(creds params.ForgeCredentials) { entityCache.UpdateCredentialsInAffectedEntities(creds) } -func GetEntitiesUsingGredentials(credsID uint) []params.ForgeEntity { - return entityCache.GetEntitiesUsingGredentials(credsID) +func GetEntitiesUsingCredentials(creds params.ForgeCredentials) []params.ForgeEntity { + return entityCache.GetEntitiesUsingCredentials(creds) } func GetAllEntities() []params.ForgeEntity { diff --git a/database/watcher/filters.go b/database/watcher/filters.go index c355890b..421fd6bf 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -188,7 +188,7 @@ func WithForgeCredentialsFilter(creds params.ForgeCredentials) dbCommon.PayloadF var idGetter params.IDGetter var ok bool switch payload.EntityType { - case dbCommon.GithubCredentialsEntityType: + case dbCommon.GithubCredentialsEntityType, dbCommon.GiteaCredentialsEntityType: idGetter, ok = payload.Payload.(params.ForgeCredentials) default: return false diff --git a/runner/pool/watcher.go b/runner/pool/watcher.go index 455f4239..97089133 100644 --- a/runner/pool/watcher.go +++ b/runner/pool/watcher.go @@ -124,7 +124,7 @@ func (r *basePoolManager) handleCredentialsUpdate(credentials params.ForgeCreden func (r *basePoolManager) handleWatcherEvent(event common.ChangePayload) { dbEntityType := common.DatabaseEntityType(r.entity.EntityType) switch event.EntityType { - case common.GithubCredentialsEntityType: + case common.GithubCredentialsEntityType, common.GiteaCredentialsEntityType: credentials, ok := event.Payload.(params.ForgeCredentials) if !ok { slog.ErrorContext(r.ctx, "failed to cast payload to github credentials") diff --git a/workers/cache/cache.go b/workers/cache/cache.go index 918b3438..8cc14224 100644 --- a/workers/cache/cache.go +++ b/workers/cache/cache.go @@ -386,8 +386,16 @@ func (w *Worker) handleCredentialsEvent(event common.ChangePayload) { } switch event.Operation { case common.CreateOperation, common.UpdateOperation: - cache.SetGithubCredentials(credentials) - entities := cache.GetEntitiesUsingGredentials(credentials.ID) + switch credentials.ForgeType { + case params.GithubEndpointType: + cache.SetGithubCredentials(credentials) + case params.GiteaEndpointType: + cache.SetGiteaCredentials(credentials) + default: + slog.DebugContext(w.ctx, "invalid credentials type", "credentials_type", credentials.ForgeType) + return + } + entities := cache.GetEntitiesUsingCredentials(credentials) for _, entity := range entities { worker, ok := w.toolsWorkes[entity.ID] if ok { @@ -414,7 +422,7 @@ func (w *Worker) handleEvent(event common.ChangePayload) { w.handleOrgEvent(event) case common.EnterpriseEntityType: w.handleEnterpriseEvent(event) - case common.GithubCredentialsEntityType: + case common.GithubCredentialsEntityType, common.GiteaCredentialsEntityType: w.handleCredentialsEvent(event) default: slog.DebugContext(w.ctx, "unknown entity type", "entity_type", event.EntityType) diff --git a/workers/entity/worker_watcher.go b/workers/entity/worker_watcher.go index f445b73d..273a8f02 100644 --- a/workers/entity/worker_watcher.go +++ b/workers/entity/worker_watcher.go @@ -17,7 +17,7 @@ func (w *Worker) handleWorkerWatcherEvent(event dbCommon.ChangePayload) { case entityType: w.handleEntityEventPayload(event) return - case dbCommon.GithubCredentialsEntityType: + case dbCommon.GithubCredentialsEntityType, dbCommon.GiteaCredentialsEntityType: slog.DebugContext(w.ctx, "got github credentials payload event") w.handleEntityCredentialsEventPayload(event) default: @@ -66,7 +66,7 @@ func (w *Worker) handleEntityCredentialsEventPayload(event dbCommon.ChangePayloa var creds params.ForgeCredentials var ok bool switch event.EntityType { - case dbCommon.GithubCredentialsEntityType: + case dbCommon.GithubCredentialsEntityType, dbCommon.GiteaCredentialsEntityType: creds, ok = event.Payload.(params.ForgeCredentials) default: slog.ErrorContext(w.ctx, "invalid entity type", "entity_type", event.EntityType) From 5dfcfc542e8de81891aa433766f29a45da18db19 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Fri, 16 May 2025 20:18:30 +0000 Subject: [PATCH 13/21] Implement webhooks install for gitea Signed-off-by: Gabriel Adrian Samfira --- database/watcher/filters.go | 2 +- util/github/client.go | 13 ++++- util/github/gitea.go | 100 ++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 util/github/gitea.go diff --git a/database/watcher/filters.go b/database/watcher/filters.go index 421fd6bf..1f747372 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -182,7 +182,7 @@ func WithEntityJobFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFunc } } -// WithGithubCredentialsFilter returns a filter function that filters payloads by Github credentials. +// WithForgeCredentialsFilter returns a filter function that filters payloads by Github credentials. func WithForgeCredentialsFilter(creds params.ForgeCredentials) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { var idGetter params.IDGetter diff --git a/util/github/client.go b/util/github/client.go index a46e4ab7..0e7fa7d6 100644 --- a/util/github/client.go +++ b/util/github/client.go @@ -92,7 +92,7 @@ func (g *githubClient) GetEntityHook(ctx context.Context, id int64) (ret *github return ret, err } -func (g *githubClient) CreateEntityHook(ctx context.Context, hook *github.Hook) (ret *github.Hook, err error) { +func (g *githubClient) createGithubEntityHook(ctx context.Context, hook *github.Hook) (ret *github.Hook, err error) { metrics.GithubOperationCount.WithLabelValues( "CreateHook", // label: operation g.entity.LabelScope(), // label: scope @@ -116,6 +116,17 @@ func (g *githubClient) CreateEntityHook(ctx context.Context, hook *github.Hook) return ret, err } +func (g *githubClient) CreateEntityHook(ctx context.Context, hook *github.Hook) (ret *github.Hook, err error) { + switch g.entity.Credentials.ForgeType { + case params.GithubEndpointType: + return g.createGithubEntityHook(ctx, hook) + case params.GiteaEndpointType: + return g.createGiteaEntityHook(ctx, hook) + default: + return nil, errors.New("invalid entity type") + } +} + func (g *githubClient) DeleteEntityHook(ctx context.Context, id int64) (ret *github.Response, err error) { metrics.GithubOperationCount.WithLabelValues( "DeleteHook", // label: operation diff --git a/util/github/gitea.go b/util/github/gitea.go new file mode 100644 index 00000000..4c83846c --- /dev/null +++ b/util/github/gitea.go @@ -0,0 +1,100 @@ +package github + +import ( + "context" + "fmt" + "net/http" + + "github.com/google/go-github/v71/github" + "github.com/pkg/errors" + + "github.com/cloudbase/garm/metrics" + "github.com/cloudbase/garm/params" +) + +type createGiteaHookOptions struct { + Type string `json:"type"` + Config map[string]string `json:"config"` + Events []string `json:"events"` + BranchFilter string `json:"branch_filter"` + Active bool `json:"active"` + AuthorizationHeader string `json:"authorization_header"` +} + +func (g *githubClient) createGiteaRepoHook(ctx context.Context, owner, name string, hook *github.Hook) (ret *github.Hook, err error) { + u := fmt.Sprintf("repos/%v/%v/hooks", owner, name) + createOpts := &createGiteaHookOptions{ + Type: "gitea", + Events: hook.Events, + Active: hook.GetActive(), + BranchFilter: "*", + Config: map[string]string{ + "content_type": hook.GetConfig().GetContentType(), + "url": hook.GetConfig().GetURL(), + "http_method": "post", + }, + } + + req, err := g.cli.NewRequest(http.MethodPost, u, createOpts) + if err != nil { + return nil, fmt.Errorf("failed to construct request: %w", err) + } + + hook = new(github.Hook) + _, err = g.cli.Do(ctx, req, hook) + if err != nil { + return nil, fmt.Errorf("request failed for %s: %w", req.URL.String(), err) + } + return hook, nil +} + +func (g *githubClient) createGiteaOrgHook(ctx context.Context, owner string, hook *github.Hook) (ret *github.Hook, err error) { + u := fmt.Sprintf("orgs/%v/hooks", owner) + createOpts := &createGiteaHookOptions{ + Type: "gitea", + Events: hook.Events, + Active: hook.GetActive(), + BranchFilter: "*", + Config: map[string]string{ + "content_type": hook.GetConfig().GetContentType(), + "url": hook.GetConfig().GetURL(), + "http_method": "post", + }, + } + + req, err := g.cli.NewRequest(http.MethodPost, u, createOpts) + if err != nil { + return nil, fmt.Errorf("failed to construct request: %w", err) + } + + hook = new(github.Hook) + _, err = g.cli.Do(ctx, req, hook) + if err != nil { + return nil, fmt.Errorf("request failed for %s: %w", req.URL.String(), err) + } + return hook, nil +} + +func (g *githubClient) createGiteaEntityHook(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.ForgeEntityTypeRepository: + ret, err = g.createGiteaRepoHook(ctx, g.entity.Owner, g.entity.Name, hook) + case params.ForgeEntityTypeOrganization: + ret, err = g.createGiteaOrgHook(ctx, g.entity.Owner, hook) + default: + return nil, errors.New("invalid entity type") + } + return ret, err +} From 6a168ba81392f13985f346c2c52498c7de64a21f Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Fri, 16 May 2025 22:43:21 +0000 Subject: [PATCH 14/21] Enable orgs Signed-off-by: Gabriel Adrian Samfira --- cmd/garm-cli/cmd/organization.go | 10 ++++-- cmd/garm/main.go | 4 +-- database/common/mocks/Store.go | 18 +++++------ database/common/store.go | 2 +- database/sql/gitea.go | 12 +++++-- database/sql/github_test.go | 2 +- database/sql/instances_test.go | 2 +- database/sql/organizations.go | 44 ++++++++++++++------------ database/sql/organizations_test.go | 17 +++------- database/sql/pools_test.go | 2 +- database/sql/scalesets_test.go | 2 +- database/sql/util.go | 18 ++++++++--- database/watcher/filters.go | 8 +++++ database/watcher/watcher_store_test.go | 2 +- params/params.go | 7 ++++ runner/organizations.go | 15 +++++++-- runner/organizations_test.go | 2 +- runner/pools_test.go | 2 +- runner/scalesets.go | 12 +++---- util/github/client.go | 2 +- workers/entity/worker.go | 2 +- workers/scaleset/controller.go | 17 +++++++--- 22 files changed, 125 insertions(+), 77 deletions(-) diff --git a/cmd/garm-cli/cmd/organization.go b/cmd/garm-cli/cmd/organization.go index c7b80fec..c35fd75b 100644 --- a/cmd/garm-cli/cmd/organization.go +++ b/cmd/garm-cli/cmd/organization.go @@ -167,6 +167,7 @@ var orgAddCmd = &cobra.Command{ Name: orgName, WebhookSecret: orgWebhookSecret, CredentialsName: orgCreds, + ForgeType: params.EndpointType(forgeType), PoolBalancerType: params.PoolBalancerType(poolBalancerType), } response, err := apiCli.Organizations.CreateOrg(newOrgReq, authToken) @@ -306,6 +307,7 @@ func init() { orgAddCmd.Flags().StringVar(&orgName, "name", "", "The name of the organization") orgAddCmd.Flags().StringVar(&poolBalancerType, "pool-balancer-type", string(params.PoolBalancerTypeRoundRobin), "The balancing strategy to use when creating runners in pools matching requested labels.") orgAddCmd.Flags().StringVar(&orgWebhookSecret, "webhook-secret", "", "The webhook secret for this organization") + orgAddCmd.Flags().StringVar(&forgeType, "forge-type", string(params.GithubEndpointType), "The forge type of the organization. Supported values: github, gitea.") orgAddCmd.Flags().StringVar(&orgCreds, "credentials", "", "Credentials name. See credentials list.") orgAddCmd.Flags().BoolVar(&orgRandomWebhookSecret, "random-webhook-secret", false, "Generate a random webhook secret for this organization.") orgAddCmd.Flags().BoolVar(&installOrgWebhook, "install-webhook", false, "Install the webhook as part of the add operation.") @@ -347,13 +349,17 @@ func formatOrganizations(orgs []params.Organization) { return } t := table.NewWriter() - header := table.Row{"ID", "Name", "Endpoint", "Credentials name", "Pool Balancer Type", "Pool mgr running"} + header := table.Row{"ID", "Name", "Endpoint", "Credentials name", "Pool Balancer Type", "Forge type", "Pool mgr running"} if long { header = append(header, "Created At", "Updated At") } t.AppendHeader(header) for _, val := range orgs { - row := table.Row{val.ID, val.Name, val.Endpoint.Name, val.CredentialsName, val.GetBalancerType(), val.PoolManagerStatus.IsRunning} + forgeType := val.Endpoint.EndpointType + if forgeType == "" { + forgeType = params.GithubEndpointType + } + row := table.Row{val.ID, val.Name, val.Endpoint.Name, val.CredentialsName, val.GetBalancerType(), forgeType, val.PoolManagerStatus.IsRunning} if long { row = append(row, val.CreatedAt, val.UpdatedAt) } diff --git a/cmd/garm/main.go b/cmd/garm/main.go index f37248d3..f0cca079 100644 --- a/cmd/garm/main.go +++ b/cmd/garm/main.go @@ -240,10 +240,10 @@ func main() { cacheWorker := cache.NewWorker(ctx, db) if err != nil { - log.Fatalf("failed to create credentials worker: %+v", err) + log.Fatalf("failed to create cache worker: %+v", err) } if err := cacheWorker.Start(); err != nil { - log.Fatalf("failed to start credentials worker: %+v", err) + log.Fatalf("failed to start cache worker: %+v", err) } providers, err := providers.LoadProvidersFromConfig(ctx, *cfg, controllerInfo.ControllerID.String()) diff --git a/database/common/mocks/Store.go b/database/common/mocks/Store.go index 30f1774c..c5994b87 100644 --- a/database/common/mocks/Store.go +++ b/database/common/mocks/Store.go @@ -348,9 +348,9 @@ func (_m *Store) CreateOrUpdateJob(ctx context.Context, job params.Job) (params. return r0, r1 } -// CreateOrganization provides a mock function with given fields: ctx, name, credentialsName, webhookSecret, poolBalancerType -func (_m *Store) CreateOrganization(ctx context.Context, name string, credentialsName string, webhookSecret string, poolBalancerType params.PoolBalancerType) (params.Organization, error) { - ret := _m.Called(ctx, name, credentialsName, webhookSecret, poolBalancerType) +// CreateOrganization provides a mock function with given fields: ctx, name, credentials, webhookSecret, poolBalancerType +func (_m *Store) CreateOrganization(ctx context.Context, name string, credentials params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (params.Organization, error) { + ret := _m.Called(ctx, name, credentials, webhookSecret, poolBalancerType) if len(ret) == 0 { panic("no return value specified for CreateOrganization") @@ -358,17 +358,17 @@ func (_m *Store) CreateOrganization(ctx context.Context, name string, credential var r0 params.Organization var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, params.PoolBalancerType) (params.Organization, error)); ok { - return rf(ctx, name, credentialsName, webhookSecret, poolBalancerType) + if rf, ok := ret.Get(0).(func(context.Context, string, params.ForgeCredentials, string, params.PoolBalancerType) (params.Organization, error)); ok { + return rf(ctx, name, credentials, webhookSecret, poolBalancerType) } - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, params.PoolBalancerType) params.Organization); ok { - r0 = rf(ctx, name, credentialsName, webhookSecret, poolBalancerType) + if rf, ok := ret.Get(0).(func(context.Context, string, params.ForgeCredentials, string, params.PoolBalancerType) params.Organization); ok { + r0 = rf(ctx, name, credentials, webhookSecret, poolBalancerType) } else { r0 = ret.Get(0).(params.Organization) } - if rf, ok := ret.Get(1).(func(context.Context, string, string, string, params.PoolBalancerType) error); ok { - r1 = rf(ctx, name, credentialsName, webhookSecret, poolBalancerType) + if rf, ok := ret.Get(1).(func(context.Context, string, params.ForgeCredentials, string, params.PoolBalancerType) error); ok { + r1 = rf(ctx, name, credentials, webhookSecret, poolBalancerType) } else { r1 = ret.Error(1) } diff --git a/database/common/store.go b/database/common/store.go index 1f5b013b..7da0e5bb 100644 --- a/database/common/store.go +++ b/database/common/store.go @@ -47,7 +47,7 @@ type RepoStore interface { } type OrgStore interface { - CreateOrganization(ctx context.Context, name, credentialsName, webhookSecret string, poolBalancerType params.PoolBalancerType) (params.Organization, error) + CreateOrganization(ctx context.Context, name string, credentials params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (org params.Organization, err error) GetOrganization(ctx context.Context, name, endpointName string) (params.Organization, error) GetOrganizationByID(ctx context.Context, orgID string) (params.Organization, error) ListOrganizations(ctx context.Context) ([]params.Organization, error) diff --git a/database/sql/gitea.go b/database/sql/gitea.go index 8d89b82d..27084a0a 100644 --- a/database/sql/gitea.go +++ b/database/sql/gitea.go @@ -268,7 +268,11 @@ func (s *sqlDatabase) getGiteaCredentialsByName(ctx context.Context, tx *gorm.DB if detailed { q = q. Preload("Repositories"). - Preload("Organizations") + Preload("Organizations"). + Preload("Repositories.GiteaCredentials"). + Preload("Organizations.GiteaCredentials"). + Preload("Repositories.Credentials"). + Preload("Organizations.Credentials") } userID, err := getUIDFromContext(ctx) @@ -304,7 +308,11 @@ func (s *sqlDatabase) GetGiteaCredentials(ctx context.Context, id uint, detailed if detailed { q = q. Preload("Repositories"). - Preload("Organizations") + Preload("Organizations"). + Preload("Repositories.GiteaCredentials"). + Preload("Organizations.GiteaCredentials"). + Preload("Repositories.Credentials"). + Preload("Organizations.Credentials") } if !auth.IsAdmin(ctx) { diff --git a/database/sql/github_test.go b/database/sql/github_test.go index 49de9aa3..2e6eb507 100644 --- a/database/sql/github_test.go +++ b/database/sql/github_test.go @@ -544,7 +544,7 @@ func (s *GithubTestSuite) TestDeleteCredentialsFailsIfReposOrgsOrEntitiesUseIt() err = s.db.DeleteRepository(ctx, repo.ID) s.Require().NoError(err) - org, err := s.db.CreateOrganization(ctx, "test-org", creds.Name, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + org, err := s.db.CreateOrganization(ctx, "test-org", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotNil(org) diff --git a/database/sql/instances_test.go b/database/sql/instances_test.go index 8610409b..c70e35dd 100644 --- a/database/sql/instances_test.go +++ b/database/sql/instances_test.go @@ -84,7 +84,7 @@ func (s *InstancesTestSuite) SetupTest() { creds := garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), githubEndpoint) // create an organization for testing purposes - org, err := s.Store.CreateOrganization(s.adminCtx, "test-org", creds.Name, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) + org, err := s.Store.CreateOrganization(s.adminCtx, "test-org", creds, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) if err != nil { s.FailNow(fmt.Sprintf("failed to create org: %s", err)) } diff --git a/database/sql/organizations.go b/database/sql/organizations.go index bf270445..6f8eaa10 100644 --- a/database/sql/organizations.go +++ b/database/sql/organizations.go @@ -29,7 +29,7 @@ import ( "github.com/cloudbase/garm/params" ) -func (s *sqlDatabase) CreateOrganization(ctx context.Context, name, credentialsName, webhookSecret string, poolBalancerType params.PoolBalancerType) (org params.Organization, err error) { +func (s *sqlDatabase) CreateOrganization(ctx context.Context, name string, credentials params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (param params.Organization, err error) { if webhookSecret == "" { return params.Organization{}, errors.New("creating org: missing secret") } @@ -40,7 +40,7 @@ func (s *sqlDatabase) CreateOrganization(ctx context.Context, name, credentialsN defer func() { if err == nil { - s.sendNotify(common.OrganizationEntityType, common.CreateOperation, org) + s.sendNotify(common.OrganizationEntityType, common.CreateOperation, param) } }() newOrg := Organization{ @@ -50,37 +50,37 @@ func (s *sqlDatabase) CreateOrganization(ctx context.Context, name, credentialsN } err = s.conn.Transaction(func(tx *gorm.DB) error { - creds, err := s.getGithubCredentialsByName(ctx, tx, credentialsName, false) - if err != nil { - return errors.Wrap(err, "creating org") + switch credentials.ForgeType { + case params.GithubEndpointType: + newOrg.CredentialsID = &credentials.ID + case params.GiteaEndpointType: + newOrg.GiteaCredentialsID = &credentials.ID + default: + return errors.Wrap(runnerErrors.ErrBadRequest, "unsupported credentials type") } - if creds.EndpointName == nil { - return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") - } - newOrg.CredentialsID = &creds.ID - newOrg.EndpointName = creds.EndpointName + newOrg.EndpointName = &credentials.Endpoint.Name q := tx.Create(&newOrg) if q.Error != nil { return errors.Wrap(q.Error, "creating org") } - - newOrg.Credentials = creds - newOrg.Endpoint = creds.Endpoint - return nil }) if err != nil { return params.Organization{}, errors.Wrap(err, "creating org") } - org, err = s.sqlToCommonOrganization(newOrg, true) + org, err := s.getOrgByID(ctx, s.conn, newOrg.ID.String(), "Pools", "Endpoint", "Credentials", "GiteaCredentials", "Credentials.Endpoint", "GiteaCredentials.Endpoint") if err != nil { return params.Organization{}, errors.Wrap(err, "creating org") } - org.WebhookSecret = webhookSecret - return org, nil + param, err = s.sqlToCommonOrganization(org, true) + if err != nil { + return params.Organization{}, errors.Wrap(err, "creating org") + } + + return param, nil } func (s *sqlDatabase) GetOrganization(ctx context.Context, name, endpointName string) (params.Organization, error) { @@ -101,7 +101,9 @@ func (s *sqlDatabase) ListOrganizations(_ context.Context) ([]params.Organizatio var orgs []Organization q := s.conn. Preload("Credentials"). + Preload("GiteaCredentials"). Preload("Credentials.Endpoint"). + Preload("GiteaCredentials.Endpoint"). Preload("Endpoint"). Find(&orgs) if q.Error != nil { @@ -121,7 +123,7 @@ func (s *sqlDatabase) ListOrganizations(_ context.Context) ([]params.Organizatio } func (s *sqlDatabase) DeleteOrganization(ctx context.Context, orgID string) (err error) { - org, err := s.getOrgByID(ctx, s.conn, orgID, "Endpoint", "Credentials", "Credentials.Endpoint") + org, err := s.getOrgByID(ctx, s.conn, orgID, "Endpoint", "Credentials", "Credentials.Endpoint", "GiteaCredentials", "GiteaCredentials.Endpoint") if err != nil { return errors.Wrap(err, "fetching org") } @@ -201,7 +203,7 @@ func (s *sqlDatabase) UpdateOrganization(ctx context.Context, orgID string, para return params.Organization{}, errors.Wrap(err, "saving org") } - org, err = s.getOrgByID(ctx, s.conn, orgID, "Endpoint", "Credentials", "Credentials.Endpoint") + org, err = s.getOrgByID(ctx, s.conn, orgID, "Endpoint", "Credentials", "Credentials.Endpoint", "GiteaCredentials", "GiteaCredentials.Endpoint") if err != nil { return params.Organization{}, errors.Wrap(err, "updating enterprise") } @@ -213,7 +215,7 @@ func (s *sqlDatabase) UpdateOrganization(ctx context.Context, orgID string, para } func (s *sqlDatabase) GetOrganizationByID(ctx context.Context, orgID string) (params.Organization, error) { - org, err := s.getOrgByID(ctx, s.conn, orgID, "Pools", "Credentials", "Endpoint", "Credentials.Endpoint") + org, err := s.getOrgByID(ctx, s.conn, orgID, "Pools", "Credentials", "Endpoint", "Credentials.Endpoint", "GiteaCredentials", "GiteaCredentials.Endpoint") if err != nil { return params.Organization{}, errors.Wrap(err, "fetching org") } @@ -254,7 +256,9 @@ func (s *sqlDatabase) getOrg(_ context.Context, name, endpointName string) (Orga q := s.conn.Where("name = ? COLLATE NOCASE and endpoint_name = ? COLLATE NOCASE", name, endpointName). Preload("Credentials"). + Preload("GiteaCredentials"). Preload("Credentials.Endpoint"). + Preload("GiteaCredentials.Endpoint"). Preload("Endpoint"). First(&org) if q.Error != nil { diff --git a/database/sql/organizations_test.go b/database/sql/organizations_test.go index a7ad23b4..3c2ba337 100644 --- a/database/sql/organizations_test.go +++ b/database/sql/organizations_test.go @@ -100,7 +100,7 @@ func (s *OrgTestSuite) SetupTest() { org, err := db.CreateOrganization( s.adminCtx, fmt.Sprintf("test-org-%d", i), - s.testCreds.Name, + s.testCreds, fmt.Sprintf("test-webhook-secret-%d", i), params.PoolBalancerTypeRoundRobin, ) @@ -179,7 +179,7 @@ func (s *OrgTestSuite) TestCreateOrganization() { org, err := s.Store.CreateOrganization( s.adminCtx, s.Fixtures.CreateOrgParams.Name, - s.Fixtures.CreateOrgParams.CredentialsName, + s.testCreds, s.Fixtures.CreateOrgParams.WebhookSecret, params.PoolBalancerTypeRoundRobin) @@ -210,7 +210,7 @@ func (s *OrgTestSuite) TestCreateOrganizationInvalidDBPassphrase() { _, err = sqlDB.CreateOrganization( s.adminCtx, s.Fixtures.CreateOrgParams.Name, - s.Fixtures.CreateOrgParams.CredentialsName, + s.testCreds, s.Fixtures.CreateOrgParams.WebhookSecret, params.PoolBalancerTypeRoundRobin) @@ -220,15 +220,6 @@ func (s *OrgTestSuite) TestCreateOrganizationInvalidDBPassphrase() { func (s *OrgTestSuite) TestCreateOrganizationDBCreateErr() { s.Fixtures.SQLMock.ExpectBegin() - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE user_id = ? AND name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). - WithArgs(s.adminUserID, s.Fixtures.Orgs[0].CredentialsName, 1). - WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). - AddRow(s.testCreds.ID, s.githubEndpoint.Name)) - s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). - WithArgs(s.testCreds.Endpoint.Name). - WillReturnRows(sqlmock.NewRows([]string{"name"}). - AddRow(s.githubEndpoint.Name)) s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `organizations`")). WillReturnError(fmt.Errorf("creating org mock error")) @@ -237,7 +228,7 @@ func (s *OrgTestSuite) TestCreateOrganizationDBCreateErr() { _, err := s.StoreSQLMocked.CreateOrganization( s.adminCtx, s.Fixtures.CreateOrgParams.Name, - s.Fixtures.CreateOrgParams.CredentialsName, + s.testCreds, s.Fixtures.CreateOrgParams.WebhookSecret, params.PoolBalancerTypeRoundRobin) diff --git a/database/sql/pools_test.go b/database/sql/pools_test.go index dfb82510..9044bf18 100644 --- a/database/sql/pools_test.go +++ b/database/sql/pools_test.go @@ -81,7 +81,7 @@ func (s *PoolsTestSuite) SetupTest() { creds := garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), githubEndpoint) // create an organization for testing purposes - org, err := s.Store.CreateOrganization(s.adminCtx, "test-org", creds.Name, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) + org, err := s.Store.CreateOrganization(s.adminCtx, "test-org", creds, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) if err != nil { s.FailNow(fmt.Sprintf("failed to create org: %s", err)) } diff --git a/database/sql/scalesets_test.go b/database/sql/scalesets_test.go index 1313af59..54937c2d 100644 --- a/database/sql/scalesets_test.go +++ b/database/sql/scalesets_test.go @@ -48,7 +48,7 @@ func (s *ScaleSetsTestSuite) SetupTest() { s.creds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), githubEndpoint) // create an organization for testing purposes - s.org, err = s.Store.CreateOrganization(s.adminCtx, "test-org", s.creds.Name, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) + s.org, err = s.Store.CreateOrganization(s.adminCtx, "test-org", s.creds, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) if err != nil { s.FailNow(fmt.Sprintf("failed to create org: %s", err)) } diff --git a/database/sql/util.go b/database/sql/util.go index 0c71261d..11d338ba 100644 --- a/database/sql/util.go +++ b/database/sql/util.go @@ -151,16 +151,24 @@ func (s *sqlDatabase) sqlToCommonOrganization(org Organization, detailed bool) ( UpdatedAt: org.UpdatedAt, } + var forgeCreds params.ForgeCredentials if org.CredentialsID != nil { ret.CredentialsID = *org.CredentialsID + forgeCreds, err = s.sqlToCommonForgeCredentials(org.Credentials) + } + + if org.GiteaCredentialsID != nil { + ret.CredentialsID = *org.GiteaCredentialsID + forgeCreds, err = s.sqlGiteaToCommonForgeCredentials(org.GiteaCredentials) + } + + if err != nil { + return params.Organization{}, errors.Wrap(err, "converting credentials") } if detailed { - creds, err := s.sqlToCommonForgeCredentials(org.Credentials) - if err != nil { - return params.Organization{}, errors.Wrap(err, "converting credentials") - } - ret.Credentials = creds + ret.Credentials = forgeCreds + ret.CredentialsName = forgeCreds.Name } if ret.PoolBalancerType == "" { diff --git a/database/watcher/filters.go b/database/watcher/filters.go index 1f747372..e462183d 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -92,6 +92,14 @@ func WithEntityPoolFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFun // in pools that belong to it. func WithEntityScaleSetFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { + forgeType, err := ghEntity.GetForgeType() + if err != nil { + return false + } + if forgeType != params.GiteaEndpointType { + return false + } + switch payload.EntityType { case dbCommon.ScaleSetEntityType: scaleSet, ok := payload.Payload.(params.ScaleSet) diff --git a/database/watcher/watcher_store_test.go b/database/watcher/watcher_store_test.go index 8791a514..3de7c01a 100644 --- a/database/watcher/watcher_store_test.go +++ b/database/watcher/watcher_store_test.go @@ -713,7 +713,7 @@ func (s *WatcherStoreTestSuite) TestOrgWatcher() { creds := garmTesting.CreateTestGithubCredentials(s.ctx, "test-creds", s.store, s.T(), ep) s.T().Cleanup(func() { s.store.DeleteGithubCredentials(s.ctx, creds.ID) }) - org, err := s.store.CreateOrganization(s.ctx, "test-org", creds.Name, "test-secret", params.PoolBalancerTypeRoundRobin) + org, err := s.store.CreateOrganization(s.ctx, "test-org", creds, "test-secret", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotEmpty(org.ID) diff --git a/params/params.go b/params/params.go index 052b2c8b..e154b2df 100644 --- a/params/params.go +++ b/params/params.go @@ -1098,6 +1098,13 @@ func (g ForgeEntity) GetCreatedAt() time.Time { return g.CreatedAt } +func (g ForgeEntity) GetForgeType() (EndpointType, error) { + if g.Credentials.ForgeType == "" { + return "", fmt.Errorf("credentials forge type is empty") + } + return g.Credentials.ForgeType, nil +} + func (g ForgeEntity) ForgeURL() string { switch g.Credentials.ForgeType { case GiteaEndpointType: diff --git a/runner/organizations.go b/runner/organizations.go index 49f143ea..bddab87c 100644 --- a/runner/organizations.go +++ b/runner/organizations.go @@ -38,7 +38,18 @@ func (r *Runner) CreateOrganization(ctx context.Context, param params.CreateOrgP return params.Organization{}, errors.Wrap(err, "validating params") } - creds, err := r.store.GetGithubCredentialsByName(ctx, param.CredentialsName, true) + var creds params.ForgeCredentials + switch param.GetForgeType() { + case params.GithubEndpointType: + slog.DebugContext(ctx, "getting github credentials") + creds, err = r.store.GetGithubCredentialsByName(ctx, param.CredentialsName, true) + case params.GiteaEndpointType: + slog.DebugContext(ctx, "getting gitea credentials") + creds, err = r.store.GetGiteaCredentialsByName(ctx, param.CredentialsName, true) + default: + return params.Organization{}, runnerErrors.NewBadRequestError("invalid forge type: %s", param.GetForgeType()) + } + if err != nil { return params.Organization{}, runnerErrors.NewBadRequestError("credentials %s not defined", param.CredentialsName) } @@ -52,7 +63,7 @@ func (r *Runner) CreateOrganization(ctx context.Context, param params.CreateOrgP return params.Organization{}, runnerErrors.NewConflictError("organization %s already exists", param.Name) } - org, err = r.store.CreateOrganization(ctx, param.Name, creds.Name, param.WebhookSecret, param.PoolBalancerType) + org, err = r.store.CreateOrganization(ctx, param.Name, creds, param.WebhookSecret, param.PoolBalancerType) if err != nil { return params.Organization{}, errors.Wrap(err, "creating organization") } diff --git a/runner/organizations_test.go b/runner/organizations_test.go index 4d4a26e1..90075c87 100644 --- a/runner/organizations_test.go +++ b/runner/organizations_test.go @@ -82,7 +82,7 @@ func (s *OrgTestSuite) SetupTest() { org, err := db.CreateOrganization( adminCtx, name, - s.testCreds.Name, + s.testCreds, fmt.Sprintf("test-webhook-secret-%v", i), params.PoolBalancerTypeRoundRobin, ) diff --git a/runner/pools_test.go b/runner/pools_test.go index 587addce..3bc5d4b3 100644 --- a/runner/pools_test.go +++ b/runner/pools_test.go @@ -69,7 +69,7 @@ func (s *PoolTestSuite) SetupTest() { s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(s.adminCtx, "secondary-creds", db, s.T(), s.githubEndpoint) // create an organization for testing purposes - org, err := db.CreateOrganization(s.adminCtx, "test-org", s.testCreds.Name, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) + org, err := db.CreateOrganization(s.adminCtx, "test-org", s.testCreds, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) if err != nil { s.FailNow(fmt.Sprintf("failed to create org: %s", err)) } diff --git a/runner/scalesets.go b/runner/scalesets.go index 6796dc2d..e7af9c22 100644 --- a/runner/scalesets.go +++ b/runner/scalesets.go @@ -16,7 +16,6 @@ package runner import ( "context" - "encoding/json" "fmt" "log/slog" @@ -181,12 +180,10 @@ func (r *Runner) UpdateScaleSetByID(ctx context.Context, scaleSetID uint, param } if hasUpdates { - result, err := scalesetCli.UpdateRunnerScaleSet(ctx, newSet.ScaleSetID, updateParams) + _, err := scalesetCli.UpdateRunnerScaleSet(ctx, newSet.ScaleSetID, updateParams) if err != nil { return fmt.Errorf("failed to update scaleset in github: %w", err) } - asJs, _ := json.MarshalIndent(result, "", " ") - slog.Info("update result", "data", string(asJs)) } return nil } @@ -216,6 +213,10 @@ func (r *Runner) CreateEntityScaleSet(ctx context.Context, entityType params.For return params.ScaleSet{}, errors.Wrap(err, "getting entity") } + if entity.Credentials.ForgeType != params.GithubEndpointType { + return params.ScaleSet{}, runnerErrors.NewBadRequestError("scale sets are only supported for github entities") + } + ghCli, err := github.Client(ctx, entity) if err != nil { return params.ScaleSet{}, errors.Wrap(err, "creating github client") @@ -255,9 +256,6 @@ func (r *Runner) CreateEntityScaleSet(ctx context.Context, entityType params.For return params.ScaleSet{}, errors.Wrap(err, "creating runner scale set") } - asJs, _ := json.MarshalIndent(runnerScaleSet, "", " ") - slog.InfoContext(ctx, "scale set", "data", string(asJs)) - defer func() { if err != nil { if innerErr := scalesetCli.DeleteRunnerScaleSet(ctx, runnerScaleSet.ID); innerErr != nil { diff --git a/util/github/client.go b/util/github/client.go index 0e7fa7d6..f25329c7 100644 --- a/util/github/client.go +++ b/util/github/client.go @@ -578,7 +578,7 @@ func Client(ctx context.Context, entity params.ForgeEntity) (common.GithubClient "upload_url", entity.Credentials.UploadBaseURL) ghClient := github.NewClient(httpClient) - switch entity.Credentials.Endpoint.EndpointType { + switch entity.Credentials.ForgeType { case params.GithubEndpointType: ghClient, err = ghClient.WithEnterpriseURLs(entity.Credentials.APIBaseURL, entity.Credentials.UploadBaseURL) case params.GiteaEndpointType: diff --git a/workers/entity/worker.go b/workers/entity/worker.go index 597d5797..efb40ac1 100644 --- a/workers/entity/worker.go +++ b/workers/entity/worker.go @@ -21,7 +21,7 @@ import ( ) func NewWorker(ctx context.Context, store dbCommon.Store, entity params.ForgeEntity, providers map[string]common.Provider) (*Worker, error) { - consumerID := fmt.Sprintf("entity-worker-%s", entity.String()) + consumerID := fmt.Sprintf("entity-worker-%s", entity.ID) ctx = garmUtil.WithSlogContext( ctx, diff --git a/workers/scaleset/controller.go b/workers/scaleset/controller.go index e28eb7ee..4aba42c2 100644 --- a/workers/scaleset/controller.go +++ b/workers/scaleset/controller.go @@ -16,7 +16,7 @@ import ( ) func NewController(ctx context.Context, store dbCommon.Store, entity params.ForgeEntity, providers map[string]common.Provider) (*Controller, error) { - consumerID := fmt.Sprintf("scaleset-controller-%s", entity.String()) + consumerID := fmt.Sprintf("scaleset-controller-%s", entity.ID) ctx = garmUtil.WithSlogContext( ctx, @@ -93,9 +93,16 @@ func (c *Controller) Start() (err error) { } c.mux.Unlock() - slog.DebugContext(c.ctx, "loaging scale sets", "entity", c.Entity.String()) - if err := c.loadAllScaleSets(); err != nil { - return fmt.Errorf("loading all scale sets: %w", err) + forgeType, err := c.Entity.GetForgeType() + if err != nil { + return fmt.Errorf("getting forge type: %w", err) + } + if forgeType == params.GithubEndpointType { + // scale sets are only available in Github + slog.DebugContext(c.ctx, "loaging scale sets", "entity", c.Entity.String()) + if err := c.loadAllScaleSets(); err != nil { + return fmt.Errorf("loading all scale sets: %w", err) + } } consumer, err := watcher.RegisterConsumer( @@ -103,7 +110,7 @@ func (c *Controller) Start() (err error) { composeControllerWatcherFilters(c.Entity), ) if err != nil { - return fmt.Errorf("registering consumer: %w", err) + return fmt.Errorf("registering consumer %q: %w", c.consumerID, err) } c.mux.Lock() From bb798a288a263d6f482b5baf0905696a872b018c Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Fri, 16 May 2025 23:58:39 +0000 Subject: [PATCH 15/21] Properly set webhook secret Signed-off-by: Gabriel Adrian Samfira --- apiserver/controllers/controllers.go | 4 ---- params/github.go | 11 ++++++++++- runner/pool/pool.go | 4 +++- runner/runner.go | 8 ++++++-- util/github/gitea.go | 2 ++ 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/apiserver/controllers/controllers.go b/apiserver/controllers/controllers.go index 32da79b3..2a57f9cf 100644 --- a/apiserver/controllers/controllers.go +++ b/apiserver/controllers/controllers.go @@ -103,7 +103,6 @@ func (a *APIController) handleWorkflowJobEvent(ctx context.Context, w http.Respo handleError(ctx, w, gErrors.NewBadRequestError("invalid post body: %s", err)) return } - slog.Debug("received workflow job event", "body", string(body)) signature := r.Header.Get("X-Hub-Signature-256") hookType := r.Header.Get("X-Github-Hook-Installation-Target-Type") @@ -162,9 +161,6 @@ func (a *APIController) WebhookHandler(w http.ResponseWriter, r *http.Request) { } headers := r.Header.Clone() - for k, v := range headers { - slog.Debug("header", "key", k, "value", v) - } event := runnerParams.Event(headers.Get("X-Github-Event")) switch event { diff --git a/params/github.go b/params/github.go index 0f963090..9859f717 100644 --- a/params/github.go +++ b/params/github.go @@ -171,7 +171,9 @@ type WorkflowJob struct { DefaultBranch string `json:"default_branch"` } `json:"repository"` Organization struct { - Login string `json:"login"` + Login string `json:"login"` + // Name is a gitea specific field + Name string `json:"name"` ID int64 `json:"id"` NodeID string `json:"node_id"` URL string `json:"url"` @@ -218,6 +220,13 @@ type WorkflowJob struct { } `json:"sender"` } +func (w WorkflowJob) GetOrgName(forgeType EndpointType) string { + if forgeType == GiteaEndpointType { + return w.Organization.Name + } + return w.Organization.Login +} + type RunnerSetting struct { Ephemeral bool `json:"ephemeral,omitempty"` IsElastic bool `json:"isElastic,omitempty"` diff --git a/runner/pool/pool.go b/runner/pool/pool.go index 8b02b593..86ce52f0 100644 --- a/runner/pool/pool.go +++ b/runner/pool/pool.go @@ -152,6 +152,7 @@ func (r *basePoolManager) getProviderBaseParams(pool params.Pool) common.Provide func (r *basePoolManager) HandleWorkflowJob(job params.WorkflowJob) error { if err := r.ValidateOwner(job); err != nil { + slog.ErrorContext(r.ctx, "failed to validate owner", "error", err) return errors.Wrap(err, "validating owner") } @@ -164,6 +165,7 @@ func (r *basePoolManager) HandleWorkflowJob(job params.WorkflowJob) error { jobParams, err := r.paramsWorkflowJobToParamsJob(job) if err != nil { + slog.ErrorContext(r.ctx, "failed to convert job to params", "error", err) return errors.Wrap(err, "converting job to params") } @@ -1962,7 +1964,7 @@ func (r *basePoolManager) ValidateOwner(job params.WorkflowJob) error { return runnerErrors.NewBadRequestError("job not meant for this pool manager") } case params.ForgeEntityTypeOrganization: - if !strings.EqualFold(job.Organization.Login, r.entity.Owner) { + if !strings.EqualFold(job.GetOrgName(r.entity.Credentials.ForgeType), r.entity.Owner) { return runnerErrors.NewBadRequestError("job not meant for this pool manager") } case params.ForgeEntityTypeEnterprise: diff --git a/runner/runner.go b/runner/runner.go index e02ee698..186799f6 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -668,8 +668,8 @@ func (r *Runner) DispatchWorkflowJob(hookTargetType, signature string, forgeType case OrganizationHook: slog.DebugContext( r.ctx, "got hook for organization", - "organization", util.SanitizeLogEntry(job.Organization.Login)) - poolManager, err = r.findOrgPoolManager(job.Organization.Login, endpoint.Name) + "organization", util.SanitizeLogEntry(job.GetOrgName(forgeType))) + poolManager, err = r.findOrgPoolManager(job.GetOrgName(forgeType), endpoint.Name) case EnterpriseHook: slog.DebugContext( r.ctx, "got hook for enterprise", @@ -679,7 +679,9 @@ func (r *Runner) DispatchWorkflowJob(hookTargetType, signature string, forgeType return runnerErrors.NewBadRequestError("cannot handle hook target type %s", hookTargetType) } + slog.DebugContext(r.ctx, "found pool manager", "pool_manager", poolManager.ID()) if err != nil { + slog.ErrorContext(r.ctx, "failed to find pool manager", "error", err, "hook_target_type", hookTargetType) // We don't have a repository or organization configured that // can handle this workflow job. return errors.Wrap(err, "fetching poolManager") @@ -689,10 +691,12 @@ func (r *Runner) DispatchWorkflowJob(hookTargetType, signature string, forgeType // we make sure that the source of this workflow job is valid. secret := poolManager.WebhookSecret() if err := r.validateHookBody(signature, secret, jobData); err != nil { + slog.ErrorContext(r.ctx, "failed to validate webhook data", "error", err) return errors.Wrap(err, "validating webhook data") } if err := poolManager.HandleWorkflowJob(job); err != nil { + slog.ErrorContext(r.ctx, "failed to handle workflow job", "error", err) return errors.Wrap(err, "handling workflow job") } diff --git a/util/github/gitea.go b/util/github/gitea.go index 4c83846c..0359836e 100644 --- a/util/github/gitea.go +++ b/util/github/gitea.go @@ -32,6 +32,7 @@ func (g *githubClient) createGiteaRepoHook(ctx context.Context, owner, name stri "content_type": hook.GetConfig().GetContentType(), "url": hook.GetConfig().GetURL(), "http_method": "post", + "secret": hook.GetConfig().GetSecret(), }, } @@ -59,6 +60,7 @@ func (g *githubClient) createGiteaOrgHook(ctx context.Context, owner string, hoo "content_type": hook.GetConfig().GetContentType(), "url": hook.GetConfig().GetURL(), "http_method": "post", + "secret": hook.GetConfig().GetSecret(), }, } From b2d56093529d9acdaee2fe59b28f69e5326449c0 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Mon, 19 May 2025 19:45:45 +0000 Subject: [PATCH 16/21] Add some tests Signed-off-by: Gabriel Adrian Samfira --- auth/context.go | 16 +- auth/instance_middleware.go | 8 +- auth/interfaces.go | 2 +- cache/cache_test.go | 400 ++++++++++++++++++++++++++- cache/tools_cache.go | 16 +- database/watcher/filters.go | 9 +- runner/pool/pool.go | 3 +- runner/runner.go | 9 +- workers/provider/instance_manager.go | 2 +- 9 files changed, 441 insertions(+), 24 deletions(-) diff --git a/auth/context.go b/auth/context.go index 0d95be56..1b648bb6 100644 --- a/auth/context.go +++ b/auth/context.go @@ -44,8 +44,21 @@ const ( instanceTokenFetched contextFlags = "tokenFetched" instanceHasJITConfig contextFlags = "hasJITConfig" instanceParams contextFlags = "instanceParams" + instanceForgeTypeKey contextFlags = "forge_type" ) +func SetInstanceForgeType(ctx context.Context, val string) context.Context { + return context.WithValue(ctx, instanceForgeTypeKey, val) +} + +func InstanceForgeType(ctx context.Context) params.EndpointType { + elem := ctx.Value(instanceForgeTypeKey) + if elem == nil { + return "" + } + return elem.(params.EndpointType) +} + func SetInstanceID(ctx context.Context, id string) context.Context { return context.WithValue(ctx, instanceIDKey, id) } @@ -159,7 +172,7 @@ func InstanceEntity(ctx context.Context) string { return elem.(string) } -func PopulateInstanceContext(ctx context.Context, instance params.Instance) context.Context { +func PopulateInstanceContext(ctx context.Context, instance params.Instance, claims *InstanceJWTClaims) context.Context { ctx = SetInstanceID(ctx, instance.ID) ctx = SetInstanceName(ctx, instance.Name) ctx = SetInstancePoolID(ctx, instance.PoolID) @@ -167,6 +180,7 @@ func PopulateInstanceContext(ctx context.Context, instance params.Instance) cont ctx = SetInstanceTokenFetched(ctx, instance.TokenFetched) ctx = SetInstanceHasJITConfig(ctx, instance.JitConfiguration) ctx = SetInstanceParams(ctx, instance) + ctx = SetInstanceForgeType(ctx, claims.ForgeType) return ctx } diff --git a/auth/instance_middleware.go b/auth/instance_middleware.go index 38e1fdcf..bcae0b0a 100644 --- a/auth/instance_middleware.go +++ b/auth/instance_middleware.go @@ -44,6 +44,7 @@ type InstanceJWTClaims struct { // Entity is the repo or org name Entity string `json:"entity"` CreateAttempt int `json:"create_attempt"` + ForgeType string `json:"forge_type"` jwt.RegisteredClaims } @@ -60,7 +61,7 @@ type instanceToken struct { jwtSecret string } -func (i *instanceToken) NewInstanceJWTToken(instance params.Instance, entity string, entityType params.ForgeEntityType, ttlMinutes uint) (string, error) { +func (i *instanceToken) NewInstanceJWTToken(instance params.Instance, entity params.ForgeEntity, entityType params.ForgeEntityType, 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. @@ -83,7 +84,8 @@ func (i *instanceToken) NewInstanceJWTToken(instance params.Instance, entity str Name: instance.Name, PoolID: instance.PoolID, Scope: entityType, - Entity: entity, + Entity: entity.String(), + ForgeType: string(entity.Credentials.ForgeType), CreateAttempt: instance.CreateAttempt, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) @@ -124,7 +126,7 @@ func (amw *instanceMiddleware) claimsToContext(ctx context.Context, claims *Inst return ctx, runnerErrors.ErrUnauthorized } - ctx = PopulateInstanceContext(ctx, instanceInfo) + ctx = PopulateInstanceContext(ctx, instanceInfo, claims) return ctx, nil } diff --git a/auth/interfaces.go b/auth/interfaces.go index 095db4b2..ab68dbd7 100644 --- a/auth/interfaces.go +++ b/auth/interfaces.go @@ -26,5 +26,5 @@ type Middleware interface { } type InstanceTokenGetter interface { - NewInstanceJWTToken(instance params.Instance, entity string, poolType params.ForgeEntityType, ttlMinutes uint) (string, error) + NewInstanceJWTToken(instance params.Instance, entity params.ForgeEntity, poolType params.ForgeEntityType, ttlMinutes uint) (string, error) } diff --git a/cache/cache_test.go b/cache/cache_test.go index 0f514329..4b7cf332 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/suite" + runnerErrors "github.com/cloudbase/garm-provider-common/errors" commonParams "github.com/cloudbase/garm-provider-common/params" garmTesting "github.com/cloudbase/garm/internal/testing" "github.com/cloudbase/garm/params" @@ -35,6 +36,7 @@ func (c *CacheTestSuite) TearDownTest() { githubToolsCache.mux.Lock() defer githubToolsCache.mux.Unlock() githubToolsCache.entities = make(map[string]GithubEntityTools) + giteaCredentialsCache.cache = make(map[uint]params.ForgeCredentials) credentialsCache.cache = make(map[uint]params.ForgeCredentials) instanceCache.cache = make(map[string]params.Instance) entityCache = &EntityCache{ @@ -49,7 +51,7 @@ func (c *CacheTestSuite) TestCacheIsInitialized() { c.Require().NotNil(entityCache) } -func (c *CacheTestSuite) TestSetCacheWorks() { +func (c *CacheTestSuite) TestSetToolsCacheWorks() { tools := []commonParams.RunnerApplicationDownload{ { DownloadURL: garmTesting.Ptr("https://example.com"), @@ -65,6 +67,39 @@ func (c *CacheTestSuite) TestSetCacheWorks() { c.Require().Equal(tools[0].GetDownloadURL(), cachedTools[0].GetDownloadURL()) } +func (c *CacheTestSuite) TestSetToolsCacheWithError() { + tools := []commonParams.RunnerApplicationDownload{ + { + DownloadURL: garmTesting.Ptr("https://example.com"), + }, + } + c.Require().NotNil(githubToolsCache) + c.Require().Len(githubToolsCache.entities, 0) + SetGithubToolsCache(c.entity, tools) + entity := githubToolsCache.entities[c.entity.ID] + + c.Require().Equal(int64(entity.expiresAt.Sub(entity.updatedAt).Minutes()), int64(60)) + c.Require().Len(githubToolsCache.entities, 1) + SetGithubToolsCacheError(c.entity, runnerErrors.ErrNotFound) + + cachedTools, err := GetGithubToolsCache(c.entity.ID) + c.Require().Error(err) + c.Require().Nil(cachedTools) +} + +func (c *CacheTestSuite) TestSetErrorOnNonExistingCacheEntity() { + entity := params.ForgeEntity{ + ID: "non-existing-entity", + } + c.Require().NotNil(githubToolsCache) + c.Require().Len(githubToolsCache.entities, 0) + SetGithubToolsCacheError(entity, runnerErrors.ErrNotFound) + + storedEntity, err := GetGithubToolsCache(entity.ID) + c.Require().Error(err) + c.Require().Nil(storedEntity) +} + func (c *CacheTestSuite) TestTimedOutToolsCache() { tools := []commonParams.RunnerApplicationDownload{ { @@ -273,12 +308,23 @@ func (c *CacheTestSuite) TestSetGetEntityCache() { c.Require().True(ok) c.Require().Equal(entity.ID, cachedEntity.ID) + pool := params.Pool{ + ID: "pool-1", + } + SetEntityPool(entity.ID, pool) + cachedEntityPools := GetEntityPools("test-entity") + c.Require().Equal(1, len(cachedEntityPools)) + entity.Credentials.Description = "test description" SetEntity(entity) cachedEntity, ok = GetEntity("test-entity") c.Require().True(ok) c.Require().Equal(entity.ID, cachedEntity.ID) c.Require().Equal(entity.Credentials.Description, cachedEntity.Credentials.Description) + + // Make sure we don't clobber pools after updating the entity + cachedEntityPools = GetEntityPools("test-entity") + c.Require().Equal(1, len(cachedEntityPools)) } func (c *CacheTestSuite) TestReplaceEntityPools() { @@ -623,6 +669,358 @@ func (c *CacheTestSuite) TestGetEntityPool() { c.Require().Equal(pool.ID, poolFromCache.ID) } +func (c *CacheTestSuite) TestSetGiteaCredentials() { + credentials := params.ForgeCredentials{ + ID: 1, + Description: "test description", + } + SetGiteaCredentials(credentials) + cachedCreds, ok := GetGiteaCredentials(1) + c.Require().True(ok) + c.Require().Equal(credentials.ID, cachedCreds.ID) + + cachedCreds.Description = "new description" + SetGiteaCredentials(cachedCreds) + cachedCreds, ok = GetGiteaCredentials(1) + c.Require().True(ok) + c.Require().Equal(credentials.ID, cachedCreds.ID) + c.Require().Equal("new description", cachedCreds.Description) +} + +func (c *CacheTestSuite) TestGetAllGiteaCredentials() { + credentials1 := params.ForgeCredentials{ + ID: 1, + } + credentials2 := params.ForgeCredentials{ + ID: 2, + } + SetGiteaCredentials(credentials1) + SetGiteaCredentials(credentials2) + + cachedCreds := GetAllGiteaCredentials() + c.Require().Len(cachedCreds, 2) + c.Require().Contains(cachedCreds, credentials1) + c.Require().Contains(cachedCreds, credentials2) +} + +func (c *CacheTestSuite) TestDeleteGiteaCredentials() { + credentials := params.ForgeCredentials{ + ID: 1, + } + SetGiteaCredentials(credentials) + cachedCreds, ok := GetGiteaCredentials(1) + c.Require().True(ok) + c.Require().Equal(credentials.ID, cachedCreds.ID) + + DeleteGiteaCredentials(1) + cachedCreds, ok = GetGiteaCredentials(1) + c.Require().False(ok) + c.Require().Equal(params.ForgeCredentials{}, cachedCreds) +} + +func (c *CacheTestSuite) TestDeleteGiteaCredentialsNotFound() { + credentials := params.ForgeCredentials{ + ID: 1, + } + SetGiteaCredentials(credentials) + cachedCreds, ok := GetGiteaCredentials(1) + c.Require().True(ok) + c.Require().Equal(credentials.ID, cachedCreds.ID) + + DeleteGiteaCredentials(2) + cachedCreds, ok = GetGiteaCredentials(1) + c.Require().True(ok) + c.Require().Equal(credentials.ID, cachedCreds.ID) +} + +func (c *CacheTestSuite) TestUpdateCredentialsInAffectedEntities() { + credentials := params.ForgeCredentials{ + ID: 1, + Description: "test description", + } + entity1 := params.ForgeEntity{ + ID: "test-entity-1", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + Credentials: credentials, + } + + entity2 := params.ForgeEntity{ + ID: "test-entity-2", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + Credentials: credentials, + } + + SetEntity(entity1) + SetEntity(entity2) + + cachedEntity1, ok := GetEntity(entity1.ID) + c.Require().True(ok) + c.Require().Equal(entity1.ID, cachedEntity1.ID) + cachedEntity2, ok := GetEntity(entity2.ID) + c.Require().True(ok) + c.Require().Equal(entity2.ID, cachedEntity2.ID) + + c.Require().Equal(credentials.ID, cachedEntity1.Credentials.ID) + c.Require().Equal(credentials.ID, cachedEntity2.Credentials.ID) + c.Require().Equal(credentials.Description, cachedEntity1.Credentials.Description) + c.Require().Equal(credentials.Description, cachedEntity2.Credentials.Description) + + credentials.Description = "new description" + SetGiteaCredentials(credentials) + + cachedEntity1, ok = GetEntity(entity1.ID) + c.Require().True(ok) + c.Require().Equal(entity1.ID, cachedEntity1.ID) + cachedEntity2, ok = GetEntity(entity2.ID) + c.Require().True(ok) + c.Require().Equal(entity2.ID, cachedEntity2.ID) + + c.Require().Equal(credentials.ID, cachedEntity1.Credentials.ID) + c.Require().Equal(credentials.ID, cachedEntity2.Credentials.ID) + c.Require().Equal(credentials.Description, cachedEntity1.Credentials.Description) + c.Require().Equal(credentials.Description, cachedEntity2.Credentials.Description) +} + +func (c *CacheTestSuite) TestSetGiteaEntity() { + credentials := params.ForgeCredentials{ + ID: 1, + Description: "test description", + ForgeType: params.GiteaEndpointType, + } + entity := params.ForgeEntity{ + ID: "test-entity", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + Credentials: credentials, + } + + SetGiteaCredentials(credentials) + SetEntity(entity) + + cachedEntity, ok := GetEntity(entity.ID) + c.Require().True(ok) + c.Require().Equal(entity.ID, cachedEntity.ID) + c.Require().Equal(credentials.ID, cachedEntity.Credentials.ID) + c.Require().Equal(credentials.Description, cachedEntity.Credentials.Description) + c.Require().Equal(credentials.ForgeType, cachedEntity.Credentials.ForgeType) +} + +func (c *CacheTestSuite) TestGetEntitiesUsingCredentials() { + credentials := params.ForgeCredentials{ + ID: 1, + Description: "test description", + Name: "test", + ForgeType: params.GithubEndpointType, + } + + credentials2 := params.ForgeCredentials{ + ID: 2, + Description: "test description2", + Name: "test", + ForgeType: params.GiteaEndpointType, + } + + entity1 := params.ForgeEntity{ + ID: "test-entity-1", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + Credentials: credentials, + } + + entity2 := params.ForgeEntity{ + ID: "test-entity-2", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + Credentials: credentials, + } + entity3 := params.ForgeEntity{ + ID: "test-entity-3", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + Credentials: credentials2, + } + + SetEntity(entity1) + SetEntity(entity2) + SetEntity(entity3) + + cachedEntities := GetEntitiesUsingCredentials(credentials) + c.Require().Len(cachedEntities, 2) + c.Require().Contains(cachedEntities, entity1) + c.Require().Contains(cachedEntities, entity2) + + cachedEntities = GetEntitiesUsingCredentials(credentials2) + c.Require().Len(cachedEntities, 1) + c.Require().Contains(cachedEntities, entity3) +} + +func (c *CacheTestSuite) TestGetallEntities() { + credentials := params.ForgeCredentials{ + ID: 1, + Description: "test description", + Name: "test", + ForgeType: params.GithubEndpointType, + } + + credentials2 := params.ForgeCredentials{ + ID: 2, + Description: "test description2", + Name: "test", + ForgeType: params.GiteaEndpointType, + } + + entity1 := params.ForgeEntity{ + ID: "test-entity-1", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + Credentials: credentials, + CreatedAt: time.Now(), + } + + entity2 := params.ForgeEntity{ + ID: "test-entity-2", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + Credentials: credentials, + CreatedAt: time.Now().Add(1 * time.Second), + } + + entity3 := params.ForgeEntity{ + ID: "test-entity-3", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + Credentials: credentials2, + CreatedAt: time.Now().Add(2 * time.Second), + } + + SetEntity(entity1) + SetEntity(entity2) + SetEntity(entity3) + + // Sorted by creation date + cachedEntities := GetAllEntities() + c.Require().Len(cachedEntities, 3) + c.Require().Equal(cachedEntities[0], entity1) + c.Require().Equal(cachedEntities[1], entity2) + c.Require().Equal(cachedEntities[2], entity3) +} + +func (c *CacheTestSuite) TestGetAllPools() { + entity := params.ForgeEntity{ + ID: "test-entity", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + } + pool1 := params.Pool{ + ID: "pool-1", + CreatedAt: time.Now(), + Tags: []params.Tag{ + { + Name: "tag1", + }, + { + Name: "tag2", + }, + }, + } + + pool2 := params.Pool{ + ID: "pool-2", + CreatedAt: time.Now().Add(1 * time.Second), + Tags: []params.Tag{ + { + Name: "tag1", + }, + { + Name: "tag3", + }, + }, + } + + SetEntity(entity) + SetEntityPool(entity.ID, pool1) + SetEntityPool(entity.ID, pool2) + cachedEntity, ok := GetEntity(entity.ID) + c.Require().True(ok) + c.Require().Equal(entity.ID, cachedEntity.ID) + pools := GetAllPools() + c.Require().Len(pools, 2) + c.Require().Equal(pools[0].ID, pool1.ID) + c.Require().Equal(pools[1].ID, pool2.ID) +} + +func (c *CacheTestSuite) TestGetAllScaleSets() { + entity := params.ForgeEntity{ + ID: "test-entity", + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + Owner: "test", + } + scaleSet1 := params.ScaleSet{ + ID: 1, + } + scaleSet2 := params.ScaleSet{ + ID: 2, + } + + SetEntity(entity) + SetEntityScaleSet(entity.ID, scaleSet1) + SetEntityScaleSet(entity.ID, scaleSet2) + cachedEntity, ok := GetEntity(entity.ID) + c.Require().True(ok) + c.Require().Equal(entity.ID, cachedEntity.ID) + scaleSets := GetAllScaleSets() + c.Require().Len(scaleSets, 2) + c.Require().Equal(scaleSets[0].ID, scaleSet1.ID) + c.Require().Equal(scaleSets[1].ID, scaleSet2.ID) +} + +func (c *CacheTestSuite) TestGetAllGetAllGithubCredentialsAsMap() { + credentials1 := params.ForgeCredentials{ + ID: 1, + } + credentials2 := params.ForgeCredentials{ + ID: 2, + } + SetGithubCredentials(credentials1) + SetGithubCredentials(credentials2) + + cachedCreds := GetAllGithubCredentialsAsMap() + c.Require().Len(cachedCreds, 2) + c.Require().Contains(cachedCreds, credentials1.ID) + c.Require().Contains(cachedCreds, credentials2.ID) +} + +func (c *CacheTestSuite) TestGetAllGiteaCredentialsAsMap() { + credentials1 := params.ForgeCredentials{ + ID: 1, + CreatedAt: time.Now(), + } + credentials2 := params.ForgeCredentials{ + ID: 2, + CreatedAt: time.Now().Add(1 * time.Second), + } + SetGiteaCredentials(credentials1) + SetGiteaCredentials(credentials2) + + cachedCreds := GetAllGiteaCredentialsAsMap() + c.Require().Len(cachedCreds, 2) + c.Require().Contains(cachedCreds, credentials1.ID) + c.Require().Contains(cachedCreds, credentials2.ID) +} + func TestCacheTestSuite(t *testing.T) { t.Parallel() suite.Run(t, new(CacheTestSuite)) diff --git a/cache/tools_cache.go b/cache/tools_cache.go index 8675bec9..6e3c4636 100644 --- a/cache/tools_cache.go +++ b/cache/tools_cache.go @@ -26,13 +26,6 @@ type GithubEntityTools struct { tools []commonParams.RunnerApplicationDownload } -func (g GithubEntityTools) Error() string { - if g.err != nil { - return g.err.Error() - } - return "" -} - type GithubToolsCache struct { mux sync.Mutex // entity IDs are UUID4s. It is highly unlikely they will collide (🤞). @@ -51,7 +44,10 @@ func (g *GithubToolsCache) Get(entityID string) ([]commonParams.RunnerApplicatio return nil, fmt.Errorf("cache expired for entity %s", entityID) } } - return cache.tools, cache.err + if cache.err != nil { + return nil, cache.err + } + return cache.tools, nil } return nil, fmt.Errorf("no cache found for entity %s", entityID) } @@ -101,3 +97,7 @@ func SetGithubToolsCache(entity params.ForgeEntity, tools []commonParams.RunnerA func GetGithubToolsCache(entityID string) ([]commonParams.RunnerApplicationDownload, error) { return githubToolsCache.Get(entityID) } + +func SetGithubToolsCacheError(entity params.ForgeEntity, err error) { + githubToolsCache.SetToolsError(entity, err) +} diff --git a/database/watcher/filters.go b/database/watcher/filters.go index e462183d..7f7a7ab9 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -87,16 +87,17 @@ func WithEntityPoolFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFun } } -// WithEntityPoolFilter returns true if the change payload is a pool that belongs to the -// supplied Github entity. This is useful when an entity worker wants to watch for changes -// in pools that belong to it. +// WithEntityScaleSetFilter returns true if the change payload is a scale set that belongs to the +// supplied Github entity. func WithEntityScaleSetFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { forgeType, err := ghEntity.GetForgeType() if err != nil { return false } - if forgeType != params.GiteaEndpointType { + + // Gitea does not have scale sets. + if forgeType == params.GiteaEndpointType { return false } diff --git a/runner/pool/pool.go b/runner/pool/pool.go index 86ce52f0..ca95867f 100644 --- a/runner/pool/pool.go +++ b/runner/pool/pool.go @@ -869,8 +869,7 @@ func (r *basePoolManager) addInstanceToProvider(instance params.Instance) error jwtValidity := pool.RunnerTimeout() - entity := r.entity.String() - jwtToken, err := r.instanceTokenGetter.NewInstanceJWTToken(instance, entity, pool.PoolType(), jwtValidity) + jwtToken, err := r.instanceTokenGetter.NewInstanceJWTToken(instance, r.entity, pool.PoolType(), jwtValidity) if err != nil { return errors.Wrap(err, "fetching instance jwt token") } diff --git a/runner/runner.go b/runner/runner.go index 186799f6..aa55ee4f 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -663,17 +663,20 @@ func (r *Runner) DispatchWorkflowJob(hookTargetType, signature string, forgeType slog.DebugContext( r.ctx, "got hook for repo", "repo_owner", util.SanitizeLogEntry(job.Repository.Owner.Login), - "repo_name", util.SanitizeLogEntry(job.Repository.Name)) + "repo_name", util.SanitizeLogEntry(job.Repository.Name), + "endpoint", endpoint.Name) poolManager, err = r.findRepoPoolManager(job.Repository.Owner.Login, job.Repository.Name, endpoint.Name) case OrganizationHook: slog.DebugContext( r.ctx, "got hook for organization", - "organization", util.SanitizeLogEntry(job.GetOrgName(forgeType))) + "organization", util.SanitizeLogEntry(job.GetOrgName(forgeType)), + "endpoint", endpoint.Name) poolManager, err = r.findOrgPoolManager(job.GetOrgName(forgeType), endpoint.Name) case EnterpriseHook: slog.DebugContext( r.ctx, "got hook for enterprise", - "enterprise", util.SanitizeLogEntry(job.Enterprise.Slug)) + "enterprise", util.SanitizeLogEntry(job.Enterprise.Slug), + "endpoint", endpoint.Name) poolManager, err = r.findEnterprisePoolManager(job.Enterprise.Slug, endpoint.Name) default: return runnerErrors.NewBadRequestError("cannot handle hook target type %s", hookTargetType) diff --git a/workers/provider/instance_manager.go b/workers/provider/instance_manager.go index d0e61b72..3900abaf 100644 --- a/workers/provider/instance_manager.go +++ b/workers/provider/instance_manager.go @@ -144,7 +144,7 @@ func (i *instanceManager) handleCreateInstanceInProvider(instance params.Instanc } token, err := i.helper.InstanceTokenGetter().NewInstanceJWTToken( - instance, entity.String(), entity.EntityType, i.scaleSet.RunnerBootstrapTimeout) + instance, entity, entity.EntityType, i.scaleSet.RunnerBootstrapTimeout) if err != nil { return fmt.Errorf("creating instance token: %w", err) } From f0753eeb22f3f41a68a204bfe9adcce830112589 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Tue, 20 May 2025 09:19:19 +0000 Subject: [PATCH 17/21] Add more tests Signed-off-by: Gabriel Adrian Samfira --- database/common/store.go | 2 +- database/sql/enterprise.go | 24 +- database/sql/enterprise_test.go | 8 +- database/sql/gitea.go | 46 +- database/sql/gitea_test.go | 793 +++++++++++++++++++++++++ database/sql/github.go | 2 +- database/sql/github_test.go | 64 +- database/sql/organizations_test.go | 60 ++ database/sql/repositories_test.go | 66 ++ database/sql/scalesets_test.go | 2 +- database/watcher/watcher_store_test.go | 2 +- internal/testing/testing.go | 41 ++ runner/enterprises.go | 2 +- runner/enterprises_test.go | 2 +- 14 files changed, 1060 insertions(+), 54 deletions(-) create mode 100644 database/sql/gitea_test.go diff --git a/database/common/store.go b/database/common/store.go index 7da0e5bb..db5fbb04 100644 --- a/database/common/store.go +++ b/database/common/store.go @@ -56,7 +56,7 @@ type OrgStore interface { } type EnterpriseStore interface { - CreateEnterprise(ctx context.Context, name, credentialsName, webhookSecret string, poolBalancerType params.PoolBalancerType) (params.Enterprise, error) + CreateEnterprise(ctx context.Context, name string, credentialsName params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (params.Enterprise, error) GetEnterprise(ctx context.Context, name, endpointName string) (params.Enterprise, error) GetEnterpriseByID(ctx context.Context, enterpriseID string) (params.Enterprise, error) ListEnterprises(ctx context.Context) ([]params.Enterprise, error) diff --git a/database/sql/enterprise.go b/database/sql/enterprise.go index 26406ac5..e9c2ed08 100644 --- a/database/sql/enterprise.go +++ b/database/sql/enterprise.go @@ -28,10 +28,14 @@ import ( "github.com/cloudbase/garm/params" ) -func (s *sqlDatabase) CreateEnterprise(ctx context.Context, name, credentialsName, webhookSecret string, poolBalancerType params.PoolBalancerType) (paramEnt params.Enterprise, err error) { +func (s *sqlDatabase) CreateEnterprise(ctx context.Context, name string, credentials params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (paramEnt params.Enterprise, err error) { if webhookSecret == "" { return params.Enterprise{}, errors.New("creating enterprise: missing secret") } + if credentials.ForgeType != params.GithubEndpointType { + return params.Enterprise{}, errors.Wrap(runnerErrors.ErrBadRequest, "enterprises are not supported on this forge type") + } + secret, err := util.Seal([]byte(webhookSecret), []byte(s.cfg.Passphrase)) if err != nil { return params.Enterprise{}, errors.Wrap(err, "encoding secret") @@ -48,24 +52,18 @@ func (s *sqlDatabase) CreateEnterprise(ctx context.Context, name, credentialsNam PoolBalancerType: poolBalancerType, } err = s.conn.Transaction(func(tx *gorm.DB) error { - creds, err := s.getGithubCredentialsByName(ctx, tx, credentialsName, false) - if err != nil { - return errors.Wrap(err, "creating enterprise") - } - if creds.EndpointName == nil { - return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") - } - newEnterprise.CredentialsID = &creds.ID - newEnterprise.EndpointName = creds.EndpointName + newEnterprise.CredentialsID = &credentials.ID + newEnterprise.EndpointName = &credentials.Endpoint.Name q := tx.Create(&newEnterprise) if q.Error != nil { return errors.Wrap(q.Error, "creating enterprise") } - newEnterprise.Credentials = creds - newEnterprise.Endpoint = creds.Endpoint - + newEnterprise, err = s.getEnterpriseByID(ctx, tx, newEnterprise.ID.String(), "Pools", "Credentials", "Endpoint", "Credentials.Endpoint") + if err != nil { + return errors.Wrap(err, "creating enterprise") + } return nil }) if err != nil { diff --git a/database/sql/enterprise_test.go b/database/sql/enterprise_test.go index 4971f78f..9d81287d 100644 --- a/database/sql/enterprise_test.go +++ b/database/sql/enterprise_test.go @@ -99,7 +99,7 @@ func (s *EnterpriseTestSuite) SetupTest() { enterprise, err := db.CreateEnterprise( s.adminCtx, fmt.Sprintf("test-enterprise-%d", i), - s.testCreds.Name, + s.testCreds, fmt.Sprintf("test-webhook-secret-%d", i), params.PoolBalancerTypeRoundRobin, ) @@ -178,7 +178,7 @@ func (s *EnterpriseTestSuite) TestCreateEnterprise() { enterprise, err := s.Store.CreateEnterprise( s.adminCtx, s.Fixtures.CreateEnterpriseParams.Name, - s.Fixtures.CreateEnterpriseParams.CredentialsName, + s.testCreds, s.Fixtures.CreateEnterpriseParams.WebhookSecret, params.PoolBalancerTypeRoundRobin) @@ -209,7 +209,7 @@ func (s *EnterpriseTestSuite) TestCreateEnterpriseInvalidDBPassphrase() { _, err = sqlDB.CreateEnterprise( s.adminCtx, s.Fixtures.CreateEnterpriseParams.Name, - s.Fixtures.CreateEnterpriseParams.CredentialsName, + s.testCreds, s.Fixtures.CreateEnterpriseParams.WebhookSecret, params.PoolBalancerTypeRoundRobin) @@ -235,7 +235,7 @@ func (s *EnterpriseTestSuite) TestCreateEnterpriseDBCreateErr() { _, err := s.StoreSQLMocked.CreateEnterprise( s.adminCtx, s.Fixtures.CreateEnterpriseParams.Name, - s.Fixtures.CreateEnterpriseParams.CredentialsName, + s.testCreds, s.Fixtures.CreateEnterpriseParams.WebhookSecret, params.PoolBalancerTypeRoundRobin) diff --git a/database/sql/gitea.go b/database/sql/gitea.go index 27084a0a..79680287 100644 --- a/database/sql/gitea.go +++ b/database/sql/gitea.go @@ -22,7 +22,7 @@ func (s *sqlDatabase) CreateGiteaEndpoint(_ context.Context, param params.Create var endpoint GithubEndpoint err = s.conn.Transaction(func(tx *gorm.DB) error { if err := tx.Where("name = ?", param.Name).First(&endpoint).Error; err == nil { - return errors.Wrap(runnerErrors.ErrDuplicateEntity, "github endpoint already exists") + return errors.Wrap(runnerErrors.ErrDuplicateEntity, "gitea endpoint already exists") } endpoint = GithubEndpoint{ Name: param.Name, @@ -34,16 +34,16 @@ func (s *sqlDatabase) CreateGiteaEndpoint(_ context.Context, param params.Create } if err := tx.Create(&endpoint).Error; err != nil { - return errors.Wrap(err, "creating github endpoint") + return errors.Wrap(err, "creating gitea endpoint") } return nil }) if err != nil { - return params.ForgeEndpoint{}, errors.Wrap(err, "creating github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "creating gitea endpoint") } ghEndpoint, err = s.sqlToCommonGithubEndpoint(endpoint) if err != nil { - return params.ForgeEndpoint{}, errors.Wrap(err, "converting github endpoint") + return params.ForgeEndpoint{}, errors.Wrap(err, "converting gitea endpoint") } return ghEndpoint, nil } @@ -52,14 +52,14 @@ func (s *sqlDatabase) ListGiteaEndpoints(_ context.Context) ([]params.ForgeEndpo var endpoints []GithubEndpoint err := s.conn.Where("endpoint_type = ?", params.GiteaEndpointType).Find(&endpoints).Error if err != nil { - return nil, errors.Wrap(err, "fetching github endpoints") + return nil, errors.Wrap(err, "fetching gitea endpoints") } var ret []params.ForgeEndpoint for _, ep := range endpoints { commonEp, err := s.sqlToCommonGithubEndpoint(ep) if err != nil { - return nil, errors.Wrap(err, "converting github endpoint") + return nil, errors.Wrap(err, "converting gitea endpoint") } ret = append(ret, commonEp) } @@ -67,10 +67,6 @@ func (s *sqlDatabase) ListGiteaEndpoints(_ context.Context) ([]params.ForgeEndpo } func (s *sqlDatabase) UpdateGiteaEndpoint(_ context.Context, name string, param params.UpdateGiteaEndpointParams) (ghEndpoint params.ForgeEndpoint, err error) { - if name == defaultGithubEndpoint { - return params.ForgeEndpoint{}, runnerErrors.NewBadRequestError("cannot update default endpoint %s", defaultGithubEndpoint) - } - defer func() { if err == nil { s.sendNotify(common.GithubEndpointEntityType, common.UpdateOperation, ghEndpoint) @@ -118,7 +114,6 @@ func (s *sqlDatabase) UpdateGiteaEndpoint(_ context.Context, name string, param func (s *sqlDatabase) GetGiteaEndpoint(_ context.Context, name string) (params.ForgeEndpoint, error) { var endpoint GithubEndpoint - err := s.conn.Where("name = ? and endpoint_type = ?", name, params.GiteaEndpointType).First(&endpoint).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -150,7 +145,7 @@ func (s *sqlDatabase) DeleteGiteaEndpoint(_ context.Context, name string) (err e } var credsCount int64 - if err := tx.Model(&GithubCredentials{}).Where("endpoint_name = ?", endpoint.Name).Count(&credsCount).Error; err != nil { + if err := tx.Model(&GiteaCredentials{}).Where("endpoint_name = ?", endpoint.Name).Count(&credsCount).Error; err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { return errors.Wrap(err, "fetching gitea credentials") } @@ -170,15 +165,8 @@ func (s *sqlDatabase) DeleteGiteaEndpoint(_ context.Context, name string) (err e } } - var entCnt int64 - if err := tx.Model(&Enterprise{}).Where("endpoint_name = ?", endpoint.Name).Count(&entCnt).Error; err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return errors.Wrap(err, "fetching gitea enterprises") - } - } - - if credsCount > 0 || repoCnt > 0 || orgCnt > 0 || entCnt > 0 { - return errors.New("cannot delete endpoint with associated entities") + if credsCount > 0 || repoCnt > 0 || orgCnt > 0 { + return runnerErrors.NewBadRequestError("cannot delete endpoint with associated entities") } if err := tx.Unscoped().Delete(&endpoint).Error; err != nil { @@ -195,7 +183,7 @@ func (s *sqlDatabase) DeleteGiteaEndpoint(_ context.Context, name string) (err e func (s *sqlDatabase) CreateGiteaCredentials(ctx context.Context, param params.CreateGiteaCredentialsParams) (gtCreds params.ForgeCredentials, err error) { userID, err := getUIDFromContext(ctx) if err != nil { - return params.ForgeCredentials{}, errors.Wrap(err, "creating github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "creating gitea credentials") } if param.Endpoint == "" { return params.ForgeCredentials{}, errors.Wrap(runnerErrors.ErrBadRequest, "endpoint name is required") @@ -211,13 +199,13 @@ func (s *sqlDatabase) CreateGiteaCredentials(ctx context.Context, param params.C var endpoint GithubEndpoint if err := tx.Where("name = ? and endpoint_type = ?", param.Endpoint, params.GiteaEndpointType).First(&endpoint).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return errors.Wrap(runnerErrors.ErrNotFound, "github endpoint not found") + return errors.Wrap(runnerErrors.ErrNotFound, "gitea endpoint not found") } - return errors.Wrap(err, "fetching github endpoint") + return errors.Wrap(err, "fetching gitea endpoint") } if err := tx.Where("name = ? and user_id = ?", param.Name, userID).First(&creds).Error; err == nil { - return errors.Wrap(runnerErrors.ErrDuplicateEntity, "github credentials already exists") + return errors.Wrap(runnerErrors.ErrDuplicateEntity, "gitea credentials already exists") } var data []byte @@ -225,8 +213,6 @@ func (s *sqlDatabase) CreateGiteaCredentials(ctx context.Context, param params.C switch param.AuthType { case params.ForgeAuthTypePAT: data, err = s.marshalAndSeal(param.PAT) - case params.ForgeAuthTypeApp: - data, err = s.marshalAndSeal(param.App) default: return errors.Wrap(runnerErrors.ErrBadRequest, "invalid auth type") } @@ -244,7 +230,7 @@ func (s *sqlDatabase) CreateGiteaCredentials(ctx context.Context, param params.C } if err := tx.Create(&creds).Error; err != nil { - return errors.Wrap(err, "creating github credentials") + return errors.Wrap(err, "creating gitea credentials") } // Skip making an extra query. creds.Endpoint = endpoint @@ -252,11 +238,11 @@ func (s *sqlDatabase) CreateGiteaCredentials(ctx context.Context, param params.C return nil }) if err != nil { - return params.ForgeCredentials{}, errors.Wrap(err, "creating github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "creating gitea credentials") } gtCreds, err = s.sqlGiteaToCommonForgeCredentials(creds) if err != nil { - return params.ForgeCredentials{}, errors.Wrap(err, "converting github credentials") + return params.ForgeCredentials{}, errors.Wrap(err, "converting gitea credentials") } return gtCreds, nil } diff --git a/database/sql/gitea_test.go b/database/sql/gitea_test.go new file mode 100644 index 00000000..c7a96d66 --- /dev/null +++ b/database/sql/gitea_test.go @@ -0,0 +1,793 @@ +// Copyright 2024 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 ( + "context" + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/suite" + + runnerErrors "github.com/cloudbase/garm-provider-common/errors" + "github.com/cloudbase/garm/auth" + "github.com/cloudbase/garm/database/common" + garmTesting "github.com/cloudbase/garm/internal/testing" + "github.com/cloudbase/garm/params" +) + +type GiteaTestSuite struct { + suite.Suite + + giteaEndpoint params.ForgeEndpoint + db common.Store +} + +func (s *GiteaTestSuite) SetupTest() { + db, err := NewSQLDatabase(context.Background(), garmTesting.GetTestSqliteDBConfig(s.T())) + if err != nil { + s.FailNow(fmt.Sprintf("failed to create db connection: %s", err)) + } + + s.db = db + + createEpParams := params.CreateGiteaEndpointParams{ + Name: testEndpointName, + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + endpoint, err := s.db.CreateGiteaEndpoint(context.Background(), createEpParams) + s.Require().NoError(err) + s.Require().NotNil(endpoint) + s.Require().Equal(testEndpointName, endpoint.Name) + s.giteaEndpoint = endpoint +} + +func (s *GiteaTestSuite) TestCreatingEndpoint() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + createEpParams := params.CreateGiteaEndpointParams{ + Name: testEndpointName, + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + + endpoint, err := s.db.CreateGiteaEndpoint(ctx, createEpParams) + s.Require().NoError(err) + s.Require().NotNil(endpoint) + s.Require().Equal(testEndpointName, endpoint.Name) +} + +func (s *GiteaTestSuite) TestCreatingDuplicateEndpointFails() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + createEpParams := params.CreateGiteaEndpointParams{ + Name: testEndpointName, + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + + _, err := s.db.CreateGiteaEndpoint(ctx, createEpParams) + s.Require().NoError(err) + + _, err = s.db.CreateGiteaEndpoint(ctx, createEpParams) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrDuplicateEntity) +} + +func (s *GiteaTestSuite) TestGetEndpoint() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + createEpParams := params.CreateGiteaEndpointParams{ + Name: "deleteme", + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + + newEndpoint, err := s.db.CreateGiteaEndpoint(ctx, createEpParams) + s.Require().NoError(err) + + endpoint, err := s.db.GetGiteaEndpoint(ctx, createEpParams.Name) + s.Require().NoError(err) + s.Require().NotNil(endpoint) + s.Require().Equal(newEndpoint.Name, endpoint.Name) +} + +func (s *GiteaTestSuite) TestGetNonExistingEndpointFailsWithNotFoundError() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + _, err := s.db.GetGiteaEndpoint(ctx, "non-existing") + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestDeletingNonExistingEndpointIsANoop() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + err := s.db.DeleteGiteaEndpoint(ctx, "non-existing") + s.Require().NoError(err) +} + +func (s *GiteaTestSuite) TestDeletingEndpoint() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + createEpParams := params.CreateGiteaEndpointParams{ + Name: testEndpointName, + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + + endpoint, err := s.db.CreateGiteaEndpoint(ctx, createEpParams) + s.Require().NoError(err) + s.Require().NotNil(endpoint) + + err = s.db.DeleteGiteaEndpoint(ctx, testEndpointName) + s.Require().NoError(err) + + _, err = s.db.GetGiteaEndpoint(ctx, testEndpointName) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestUpdateEndpoint() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + createEpParams := params.CreateGiteaEndpointParams{ + Name: "deleteme", + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + + endpoint, err := s.db.CreateGiteaEndpoint(ctx, createEpParams) + s.Require().NoError(err) + s.Require().NotNil(endpoint) + + newDescription := "new description" + newAPIBaseURL := "https://new-api.example.com" + newBaseURL := "https://new.example.com" + caCertBundle, err := os.ReadFile("../../testdata/certs/srv-pub.pem") + s.Require().NoError(err) + updateEpParams := params.UpdateGiteaEndpointParams{ + Description: &newDescription, + APIBaseURL: &newAPIBaseURL, + BaseURL: &newBaseURL, + CACertBundle: caCertBundle, + } + + updatedEndpoint, err := s.db.UpdateGiteaEndpoint(ctx, testEndpointName, updateEpParams) + s.Require().NoError(err) + s.Require().NotNil(updatedEndpoint) + s.Require().Equal(newDescription, updatedEndpoint.Description) + s.Require().Equal(newAPIBaseURL, updatedEndpoint.APIBaseURL) + s.Require().Equal(newBaseURL, updatedEndpoint.BaseURL) + s.Require().Equal(caCertBundle, updatedEndpoint.CACertBundle) +} + +func (s *GiteaTestSuite) TestUpdatingNonExistingEndpointReturnsNotFoundError() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + newDescription := "test" + updateEpParams := params.UpdateGiteaEndpointParams{ + Description: &newDescription, + } + + _, err := s.db.UpdateGiteaEndpoint(ctx, "non-existing", updateEpParams) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestListEndpoints() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + createEpParams := params.CreateGiteaEndpointParams{ + Name: testEndpointName, + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + + _, err := s.db.CreateGiteaEndpoint(ctx, createEpParams) + s.Require().NoError(err) + + endpoints, err := s.db.ListGiteaEndpoints(ctx) + s.Require().NoError(err) + s.Require().Len(endpoints, 1) +} + +func (s *GiteaTestSuite) TestCreateCredentialsFailsWithUnauthorizedForAnonUser() { + ctx := context.Background() + + _, err := s.db.CreateGiteaCredentials(ctx, params.CreateGiteaCredentialsParams{}) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrUnauthorized) +} + +func (s *GiteaTestSuite) TestCreateCredentialsFailsWhenEndpointNameIsEmpty() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + _, err := s.db.CreateGiteaCredentials(ctx, params.CreateGiteaCredentialsParams{}) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrBadRequest) + s.Require().Regexp("endpoint name is required", err.Error()) +} + +func (s *GiteaTestSuite) TestCreateCredentialsFailsWhenEndpointDoesNotExist() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + _, err := s.db.CreateGiteaCredentials(ctx, params.CreateGiteaCredentialsParams{Endpoint: "non-existing"}) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) + s.Require().Regexp("endpoint not found", err.Error()) +} + +func (s *GiteaTestSuite) TestCreateCredentialsFailsWhenAuthTypeIsInvalid() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + _, err := s.db.CreateGiteaCredentials(ctx, params.CreateGiteaCredentialsParams{Endpoint: s.giteaEndpoint.Name, AuthType: "invalid"}) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrBadRequest) + s.Require().Regexp("invalid auth type", err.Error()) +} + +func (s *GiteaTestSuite) TestCreateCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + s.Require().Equal(credParams.Name, creds.Name) + s.Require().Equal(credParams.Description, creds.Description) + s.Require().Equal(credParams.Endpoint, creds.Endpoint.Name) + s.Require().Equal(credParams.AuthType, creds.AuthType) +} + +func (s *GiteaTestSuite) TestCreateCredentialsFailsOnDuplicateCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + testUser := garmTesting.CreateGARMTestUser(ctx, "testuser", s.db, s.T()) + testUserCtx := auth.PopulateContext(context.Background(), testUser, nil) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test", + }, + } + + _, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + + // Creating creds with the same parameters should fail for the same user. + _, err = s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrDuplicateEntity) + + // Creating creds with the same parameters should work for different users. + _, err = s.db.CreateGiteaCredentials(testUserCtx, credParams) + s.Require().NoError(err) +} + +func (s *GiteaTestSuite) TestNormalUsersCanOnlySeeTheirOwnCredentialsAdminCanSeeAll() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + testUser := garmTesting.CreateGARMTestUser(ctx, "testuser1", s.db, s.T()) + testUser2 := garmTesting.CreateGARMTestUser(ctx, "testuser2", s.db, s.T()) + testUserCtx := auth.PopulateContext(context.Background(), testUser, nil) + testUser2Ctx := auth.PopulateContext(context.Background(), testUser2, nil) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + credParams.Name = "test-creds2" + creds2, err := s.db.CreateGiteaCredentials(testUserCtx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds2) + + credParams.Name = "test-creds3" + creds3, err := s.db.CreateGiteaCredentials(testUser2Ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds3) + + credsList, err := s.db.ListGiteaCredentials(ctx) + s.Require().NoError(err) + s.Require().Len(credsList, 3) + + credsList, err = s.db.ListGiteaCredentials(testUserCtx) + s.Require().NoError(err) + s.Require().Len(credsList, 1) + s.Require().Equal("test-creds2", credsList[0].Name) + + credsList, err = s.db.ListGiteaCredentials(testUser2Ctx) + s.Require().NoError(err) + s.Require().Len(credsList, 1) + s.Require().Equal("test-creds3", credsList[0].Name) +} + +func (s *GiteaTestSuite) TestGetGiteaCredentialsFailsWhenCredentialsDontExist() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + _, err := s.db.GetGiteaCredentials(ctx, 1, true) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) + + _, err = s.db.GetGiteaCredentialsByName(ctx, "non-existing", true) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestGetGithubCredentialsByNameReturnsOnlyCurrentUserCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + testUser := garmTesting.CreateGARMTestUser(ctx, "test-user1", s.db, s.T()) + testUserCtx := auth.PopulateContext(context.Background(), testUser, nil) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + creds2, err := s.db.CreateGiteaCredentials(testUserCtx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds2) + + creds2Get, err := s.db.GetGiteaCredentialsByName(testUserCtx, testCredsName, true) + s.Require().NoError(err) + s.Require().NotNil(creds2) + s.Require().Equal(testCredsName, creds2Get.Name) + s.Require().Equal(creds2.ID, creds2Get.ID) + + credsGet, err := s.db.GetGiteaCredentialsByName(ctx, testCredsName, true) + s.Require().NoError(err) + s.Require().NotNil(creds) + s.Require().Equal(testCredsName, credsGet.Name) + s.Require().Equal(creds.ID, credsGet.ID) + + // Admin can get any creds by ID + credsGet, err = s.db.GetGiteaCredentials(ctx, creds2.ID, true) + s.Require().NoError(err) + s.Require().NotNil(creds2) + s.Require().Equal(creds2.ID, credsGet.ID) + + // Normal user cannot get other user creds by ID + _, err = s.db.GetGiteaCredentials(testUserCtx, creds.ID, true) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestGetGithubCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + creds2, err := s.db.GetGiteaCredentialsByName(ctx, testCredsName, true) + s.Require().NoError(err) + s.Require().NotNil(creds2) + s.Require().Equal(creds.Name, creds2.Name) + s.Require().Equal(creds.ID, creds2.ID) + + creds2, err = s.db.GetGiteaCredentials(ctx, creds.ID, true) + s.Require().NoError(err) + s.Require().NotNil(creds2) + s.Require().Equal(creds.Name, creds2.Name) + s.Require().Equal(creds.ID, creds2.ID) +} + +func (s *GiteaTestSuite) TestDeleteGiteaCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + err = s.db.DeleteGiteaCredentials(ctx, creds.ID) + s.Require().NoError(err) + + _, err = s.db.GetGiteaCredentials(ctx, creds.ID, true) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestDeleteGiteaCredentialsByNonAdminUser() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + testUser := garmTesting.CreateGARMTestUser(ctx, "test-user4", s.db, s.T()) + testUserCtx := auth.PopulateContext(context.Background(), testUser, nil) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test-creds4", + }, + } + + // Create creds as admin + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + // Deleting non existent creds will return a nil error. For the test user + // the creds created by the admin should not be visible, which leads to not found + // which in turn returns no error. + err = s.db.DeleteGiteaCredentials(testUserCtx, creds.ID) + s.Require().NoError(err) + + // Check that the creds created by the admin are still there. + credsGet, err := s.db.GetGiteaCredentials(ctx, creds.ID, true) + s.Require().NoError(err) + s.Require().NotNil(credsGet) + s.Require().Equal(creds.ID, credsGet.ID) + + // Create the same creds with the test user. + creds2, err := s.db.CreateGiteaCredentials(testUserCtx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds2) + + // Remove creds created by test user. + err = s.db.DeleteGiteaCredentials(testUserCtx, creds2.ID) + s.Require().NoError(err) + + // The creds created by the test user should be gone. + _, err = s.db.GetGiteaCredentials(testUserCtx, creds2.ID, true) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestDeleteCredentialsFailsIfReposOrgsOrEntitiesUseIt() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + repo, err := s.db.CreateRepository(ctx, "test-owner", "test-repo", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + s.Require().NotNil(repo) + + err = s.db.DeleteGiteaCredentials(ctx, creds.ID) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrBadRequest) + + err = s.db.DeleteRepository(ctx, repo.ID) + s.Require().NoError(err) + + org, err := s.db.CreateOrganization(ctx, "test-org", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + s.Require().NotNil(org) + + err = s.db.DeleteGiteaCredentials(ctx, creds.ID) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrBadRequest) + + err = s.db.DeleteOrganization(ctx, org.ID) + s.Require().NoError(err) + + enterprise, err := s.db.CreateEnterprise(ctx, "test-enterprise", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + s.Require().ErrorIs(err, runnerErrors.ErrBadRequest) + s.Require().Equal(params.Enterprise{}, enterprise) + + err = s.db.DeleteGiteaCredentials(ctx, creds.ID) + s.Require().NoError(err) + + _, err = s.db.GetGiteaCredentials(ctx, creds.ID, true) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestUpdateCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + newDescription := "new description" + newName := "new-name" + newToken := "new-token" + updateCredParams := params.UpdateGiteaCredentialsParams{ + Description: &newDescription, + Name: &newName, + PAT: ¶ms.GithubPAT{ + OAuth2Token: newToken, + }, + } + + updatedCreds, err := s.db.UpdateGiteaCredentials(ctx, creds.ID, updateCredParams) + s.Require().NoError(err) + s.Require().NotNil(updatedCreds) + s.Require().Equal(newDescription, updatedCreds.Description) + s.Require().Equal(newName, updatedCreds.Name) +} + +func (s *GiteaTestSuite) TestUpdateCredentialsFailsForNonExistingCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + updateCredParams := params.UpdateGiteaCredentialsParams{ + Description: nil, + } + + _, err := s.db.UpdateGiteaCredentials(ctx, 1, updateCredParams) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestUpdateCredentialsFailsIfCredentialsAreOwnedByNonAdminUser() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + testUser := garmTesting.CreateGARMTestUser(ctx, "test-user5", s.db, s.T()) + testUserCtx := auth.PopulateContext(context.Background(), testUser, nil) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test-creds5", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + newDescription := "new description2" + updateCredParams := params.UpdateGiteaCredentialsParams{ + Description: &newDescription, + } + + _, err = s.db.UpdateGiteaCredentials(testUserCtx, creds.ID, updateCredParams) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestAdminUserCanUpdateAnyGiteaCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + testUser := garmTesting.CreateGARMTestUser(ctx, "test-user5", s.db, s.T()) + testUserCtx := auth.PopulateContext(context.Background(), testUser, nil) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test-creds5", + }, + } + + creds, err := s.db.CreateGiteaCredentials(testUserCtx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + newDescription := "new description2" + updateCredParams := params.UpdateGiteaCredentialsParams{ + Description: &newDescription, + } + + newCreds, err := s.db.UpdateGiteaCredentials(ctx, creds.ID, updateCredParams) + s.Require().NoError(err) + s.Require().Equal(newDescription, newCreds.Description) +} + +func (s *GiteaTestSuite) TestDeleteCredentialsWithOrgsOrReposFails() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: s.giteaEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test-creds5", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + repo, err := s.db.CreateRepository(ctx, "test-owner", "test-repo", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + s.Require().NotNil(repo) + + err = s.db.DeleteGiteaCredentials(ctx, creds.ID) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrBadRequest) + + err = s.db.DeleteRepository(ctx, repo.ID) + s.Require().NoError(err) + + org, err := s.db.CreateOrganization(ctx, "test-org", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + s.Require().NotNil(org) + + err = s.db.DeleteGiteaCredentials(ctx, creds.ID) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrBadRequest) + + err = s.db.DeleteOrganization(ctx, org.ID) + s.Require().NoError(err) + + err = s.db.DeleteGiteaCredentials(ctx, creds.ID) + s.Require().NoError(err) + + _, err = s.db.GetGiteaCredentials(ctx, creds.ID, true) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestDeleteGiteaEndpointFailsWithOrgsReposOrCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + endpointParams := params.CreateGiteaEndpointParams{ + Name: "deleteme", + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + + ep, err := s.db.CreateGiteaEndpoint(ctx, endpointParams) + s.Require().NoError(err) + s.Require().NotNil(ep) + + credParams := params.CreateGiteaCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: ep.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test-creds5", + }, + } + + creds, err := s.db.CreateGiteaCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + repo, err := s.db.CreateRepository(ctx, "test-owner", "test-repo", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + s.Require().NotNil(repo) + + badRequest := &runnerErrors.BadRequestError{} + err = s.db.DeleteGiteaEndpoint(ctx, ep.Name) + s.Require().Error(err) + s.Require().ErrorAs(err, &badRequest) + + err = s.db.DeleteRepository(ctx, repo.ID) + s.Require().NoError(err) + + org, err := s.db.CreateOrganization(ctx, "test-org", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + s.Require().NotNil(org) + + err = s.db.DeleteGiteaEndpoint(ctx, ep.Name) + s.Require().Error(err) + s.Require().ErrorAs(err, &badRequest) + + err = s.db.DeleteOrganization(ctx, org.ID) + s.Require().NoError(err) + + err = s.db.DeleteGiteaCredentials(ctx, creds.ID) + s.Require().NoError(err) + + err = s.db.DeleteGiteaEndpoint(ctx, ep.Name) + s.Require().NoError(err) + + _, err = s.db.GetGiteaEndpoint(ctx, ep.Name) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + +func (s *GiteaTestSuite) TestListGiteaEndpoints() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + createEpParams := params.CreateGiteaEndpointParams{ + Name: "deleteme", + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + + _, err := s.db.CreateGiteaEndpoint(ctx, createEpParams) + s.Require().NoError(err) + + endpoints, err := s.db.ListGiteaEndpoints(ctx) + s.Require().NoError(err) + s.Require().Len(endpoints, 2) +} + +func TestGiteaTestSuite(t *testing.T) { + suite.Run(t, new(GiteaTestSuite)) +} diff --git a/database/sql/github.go b/database/sql/github.go index 08d6bdb3..8dd20225 100644 --- a/database/sql/github.go +++ b/database/sql/github.go @@ -200,7 +200,7 @@ func (s *sqlDatabase) DeleteGithubEndpoint(_ context.Context, name string) (err } if credsCount > 0 || repoCnt > 0 || orgCnt > 0 || entCnt > 0 { - return errors.New("cannot delete endpoint with associated entities") + return runnerErrors.NewBadRequestError("cannot delete endpoint with associated entities") } if err := tx.Unscoped().Delete(&endpoint).Error; err != nil { diff --git a/database/sql/github_test.go b/database/sql/github_test.go index 2e6eb507..f2e83fec 100644 --- a/database/sql/github_test.go +++ b/database/sql/github_test.go @@ -555,7 +555,7 @@ func (s *GithubTestSuite) TestDeleteCredentialsFailsIfReposOrgsOrEntitiesUseIt() err = s.db.DeleteOrganization(ctx, org.ID) s.Require().NoError(err) - enterprise, err := s.db.CreateEnterprise(ctx, "test-enterprise", creds.Name, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + enterprise, err := s.db.CreateEnterprise(ctx, "test-enterprise", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotNil(enterprise) @@ -737,6 +737,68 @@ func (s *GithubTestSuite) TestAdminUserCanUpdateAnyGithubCredentials() { s.Require().Equal(newDescription, newCreds.Description) } +func (s *GithubTestSuite) TestDeleteGithubEndpointFailsWithOrgsReposOrCredentials() { + ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) + + endpointParams := params.CreateGithubEndpointParams{ + Name: "deleteme", + Description: testEndpointDescription, + APIBaseURL: testAPIBaseURL, + BaseURL: testBaseURL, + } + + ep, err := s.db.CreateGithubEndpoint(ctx, endpointParams) + s.Require().NoError(err) + s.Require().NotNil(ep) + + credParams := params.CreateGithubCredentialsParams{ + Name: testCredsName, + Description: testCredsDescription, + Endpoint: ep.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "test-creds5", + }, + } + + creds, err := s.db.CreateGithubCredentials(ctx, credParams) + s.Require().NoError(err) + s.Require().NotNil(creds) + + repo, err := s.db.CreateRepository(ctx, "test-owner", "test-repo", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + s.Require().NotNil(repo) + + badRequest := &runnerErrors.BadRequestError{} + err = s.db.DeleteGithubEndpoint(ctx, ep.Name) + s.Require().Error(err) + s.Require().ErrorAs(err, &badRequest) + + err = s.db.DeleteRepository(ctx, repo.ID) + s.Require().NoError(err) + + org, err := s.db.CreateOrganization(ctx, "test-org", creds, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + s.Require().NotNil(org) + + err = s.db.DeleteGithubEndpoint(ctx, ep.Name) + s.Require().Error(err) + s.Require().ErrorAs(err, &badRequest) + + err = s.db.DeleteOrganization(ctx, org.ID) + s.Require().NoError(err) + + err = s.db.DeleteGithubCredentials(ctx, creds.ID) + s.Require().NoError(err) + + err = s.db.DeleteGithubEndpoint(ctx, ep.Name) + s.Require().NoError(err) + + _, err = s.db.GetGithubEndpoint(ctx, ep.Name) + s.Require().Error(err) + s.Require().ErrorIs(err, runnerErrors.ErrNotFound) +} + func TestGithubTestSuite(t *testing.T) { suite.Run(t, new(GithubTestSuite)) } diff --git a/database/sql/organizations_test.go b/database/sql/organizations_test.go index 3c2ba337..a93ef372 100644 --- a/database/sql/organizations_test.go +++ b/database/sql/organizations_test.go @@ -54,8 +54,10 @@ type OrgTestSuite struct { adminUserID string testCreds params.ForgeCredentials + testCredsGitea params.ForgeCredentials secondaryTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint + giteaEndpoint params.ForgeEndpoint } func (s *OrgTestSuite) equalInstancesByName(expected, actual []params.Instance) { @@ -91,7 +93,9 @@ func (s *OrgTestSuite) SetupTest() { s.Require().NotEmpty(s.adminUserID) s.githubEndpoint = garmTesting.CreateDefaultGithubEndpoint(adminCtx, db, s.T()) + s.giteaEndpoint = garmTesting.CreateDefaultGiteaEndpoint(adminCtx, db, s.T()) s.testCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), s.githubEndpoint) + s.testCredsGitea = garmTesting.CreateTestGiteaCredentials(adminCtx, "new-creds", db, s.T(), s.giteaEndpoint) s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "secondary-creds", db, s.T(), s.githubEndpoint) // create some organization objects in the database, for testing purposes @@ -192,6 +196,62 @@ func (s *OrgTestSuite) TestCreateOrganization() { s.Require().Equal(storeOrg.Name, org.Name) s.Require().Equal(storeOrg.Credentials.Name, org.Credentials.Name) s.Require().Equal(storeOrg.WebhookSecret, org.WebhookSecret) + + entity, err := org.GetEntity() + s.Require().Nil(err) + s.Require().Equal(entity.EntityType, params.ForgeEntityTypeOrganization) + s.Require().Equal(entity.ID, org.ID) + + forgeType, err := entity.GetForgeType() + s.Require().Nil(err) + s.Require().Equal(forgeType, params.GithubEndpointType) +} + +func (s *OrgTestSuite) TestCreateOrgForGitea() { + // call tested function + org, err := s.Store.CreateOrganization( + s.adminCtx, + s.Fixtures.CreateOrgParams.Name, + s.testCredsGitea, + s.Fixtures.CreateOrgParams.WebhookSecret, + params.PoolBalancerTypeRoundRobin) + + // assertions + s.Require().Nil(err) + storeOrg, err := s.Store.GetOrganizationByID(s.adminCtx, org.ID) + if err != nil { + s.FailNow(fmt.Sprintf("failed to get organization by id: %v", err)) + } + s.Require().Equal(storeOrg.Name, org.Name) + s.Require().Equal(storeOrg.Credentials.Name, org.Credentials.Name) + s.Require().Equal(storeOrg.WebhookSecret, org.WebhookSecret) + + entity, err := org.GetEntity() + s.Require().Nil(err) + s.Require().Equal(entity.EntityType, params.ForgeEntityTypeOrganization) + s.Require().Equal(entity.ID, org.ID) + + forgeType, err := entity.GetForgeType() + s.Require().Nil(err) + s.Require().Equal(forgeType, params.GiteaEndpointType) +} + +func (s *OrgTestSuite) TestCreateOrganizationInvalidForgeType() { + credentials := params.ForgeCredentials{ + Name: "test-creds", + Endpoint: s.githubEndpoint, + ID: 99, + ForgeType: params.EndpointType("invalid-forge-type"), + } + + _, err := s.Store.CreateOrganization( + s.adminCtx, + s.Fixtures.CreateOrgParams.Name, + credentials, + s.Fixtures.CreateOrgParams.WebhookSecret, + params.PoolBalancerTypeRoundRobin) + s.Require().NotNil(err) + s.Require().Equal("creating org: unsupported credentials type: invalid request", err.Error()) } func (s *OrgTestSuite) TestCreateOrganizationInvalidDBPassphrase() { diff --git a/database/sql/repositories_test.go b/database/sql/repositories_test.go index 0e2fa08f..3f8d8ca2 100644 --- a/database/sql/repositories_test.go +++ b/database/sql/repositories_test.go @@ -59,8 +59,10 @@ type RepoTestSuite struct { adminUserID string testCreds params.ForgeCredentials + testCredsGitea params.ForgeCredentials secondaryTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint + giteaEndpoint params.ForgeEndpoint } func (s *RepoTestSuite) equalReposByName(expected, actual []params.Repository) { @@ -109,7 +111,9 @@ func (s *RepoTestSuite) SetupTest() { s.Require().NotEmpty(s.adminUserID) s.githubEndpoint = garmTesting.CreateDefaultGithubEndpoint(adminCtx, db, s.T()) + s.giteaEndpoint = garmTesting.CreateDefaultGiteaEndpoint(adminCtx, db, s.T()) s.testCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), s.githubEndpoint) + s.testCredsGitea = garmTesting.CreateTestGiteaCredentials(adminCtx, "new-creds", db, s.T(), s.giteaEndpoint) s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "secondary-creds", db, s.T(), s.githubEndpoint) // create some repository objects in the database, for testing purposes @@ -219,6 +223,68 @@ func (s *RepoTestSuite) TestCreateRepository() { s.Require().Equal(storeRepo.Name, repo.Name) s.Require().Equal(storeRepo.Credentials.Name, repo.Credentials.Name) s.Require().Equal(storeRepo.WebhookSecret, repo.WebhookSecret) + + entity, err := repo.GetEntity() + s.Require().Nil(err) + s.Require().Equal(s.Fixtures.CreateRepoParams.Owner, entity.ID) + s.Require().Equal(entity.EntityType, params.ForgeEntityTypeRepository) + + forgeType, err := entity.GetForgeType() + s.Require().Nil(err) + s.Require().Equal(forgeType, params.GithubEndpointType) +} + +func (s *RepoTestSuite) TestCreateRepositoryGitea() { + // call tested function + repo, err := s.Store.CreateRepository( + s.adminCtx, + s.Fixtures.CreateRepoParams.Owner, + s.Fixtures.CreateRepoParams.Name, + s.testCredsGitea, + s.Fixtures.CreateRepoParams.WebhookSecret, + params.PoolBalancerTypeRoundRobin, + ) + + // assertions + s.Require().Nil(err) + storeRepo, err := s.Store.GetRepositoryByID(s.adminCtx, repo.ID) + if err != nil { + s.FailNow(fmt.Sprintf("failed to get repository by id: %v", err)) + } + s.Require().Equal(storeRepo.Owner, repo.Owner) + s.Require().Equal(storeRepo.Name, repo.Name) + s.Require().Equal(storeRepo.Credentials.Name, repo.Credentials.Name) + s.Require().Equal(storeRepo.WebhookSecret, repo.WebhookSecret) + + entity, err := repo.GetEntity() + s.Require().Nil(err) + s.Require().Equal(repo.ID, entity.ID) + s.Require().Equal(entity.EntityType, params.ForgeEntityTypeRepository) + + forgeType, err := entity.GetForgeType() + s.Require().Nil(err) + s.Require().Equal(forgeType, params.GiteaEndpointType) +} + +func (s *RepoTestSuite) TestCreateRepositoryInvalidForgeType() { + // call tested function + _, err := s.Store.CreateRepository( + s.adminCtx, + s.Fixtures.CreateRepoParams.Owner, + s.Fixtures.CreateRepoParams.Name, + params.ForgeCredentials{ + Name: "test-creds", + ForgeType: "invalid-forge-type", + Endpoint: params.ForgeEndpoint{ + Name: "test-endpoint", + }, + }, + s.Fixtures.CreateRepoParams.WebhookSecret, + params.PoolBalancerTypeRoundRobin, + ) + + s.Require().NotNil(err) + s.Require().Equal("creating repository: unsupported credentials type: invalid request", err.Error()) } func (s *RepoTestSuite) TestCreateRepositoryInvalidDBPassphrase() { diff --git a/database/sql/scalesets_test.go b/database/sql/scalesets_test.go index 54937c2d..7d49b397 100644 --- a/database/sql/scalesets_test.go +++ b/database/sql/scalesets_test.go @@ -58,7 +58,7 @@ func (s *ScaleSetsTestSuite) SetupTest() { s.FailNow(fmt.Sprintf("failed to create repo: %s", err)) } - s.enterprise, err = s.Store.CreateEnterprise(s.adminCtx, "test-enterprise", s.creds.Name, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) + s.enterprise, err = s.Store.CreateEnterprise(s.adminCtx, "test-enterprise", s.creds, "test-webhookSecret", params.PoolBalancerTypeRoundRobin) if err != nil { s.FailNow(fmt.Sprintf("failed to create enterprise: %s", err)) } diff --git a/database/watcher/watcher_store_test.go b/database/watcher/watcher_store_test.go index 3de7c01a..7045455e 100644 --- a/database/watcher/watcher_store_test.go +++ b/database/watcher/watcher_store_test.go @@ -646,7 +646,7 @@ func (s *WatcherStoreTestSuite) TestEnterpriseWatcher() { creds := garmTesting.CreateTestGithubCredentials(s.ctx, "test-creds", s.store, s.T(), ep) s.T().Cleanup(func() { s.store.DeleteGithubCredentials(s.ctx, creds.ID) }) - ent, err := s.store.CreateEnterprise(s.ctx, "test-enterprise", creds.Name, "test-secret", params.PoolBalancerTypeRoundRobin) + ent, err := s.store.CreateEnterprise(s.ctx, "test-enterprise", creds, "test-secret", params.PoolBalancerTypeRoundRobin) s.Require().NoError(err) s.Require().NotEmpty(ent.ID) diff --git a/internal/testing/testing.go b/internal/testing/testing.go index 0fcc1dda..84b4d48c 100644 --- a/internal/testing/testing.go +++ b/internal/testing/testing.go @@ -110,6 +110,30 @@ func CreateDefaultGithubEndpoint(ctx context.Context, db common.Store, s *testin return ep } +func CreateDefaultGiteaEndpoint(ctx context.Context, db common.Store, s *testing.T) params.ForgeEndpoint { + endpointParams := params.CreateGiteaEndpointParams{ + Name: "gitea.example.com", + Description: "gitea endpoint", + APIBaseURL: "https://gitea.example.com/", + BaseURL: "https://gitea.example.com/", + } + + ep, err := db.GetGithubEndpoint(ctx, endpointParams.Name) + if err != nil { + if !errors.Is(err, runnerErrors.ErrNotFound) { + s.Fatalf("failed to get database object (github.com): %v", err) + } + ep, err = db.CreateGiteaEndpoint(ctx, endpointParams) + if err != nil { + if !errors.Is(err, runnerErrors.ErrDuplicateEntity) { + s.Fatalf("failed to create database object (github.com): %v", err) + } + } + } + + return ep +} + func CreateTestGithubCredentials(ctx context.Context, credsName string, db common.Store, s *testing.T, endpoint params.ForgeEndpoint) params.ForgeCredentials { newCredsParams := params.CreateGithubCredentialsParams{ Name: credsName, @@ -127,6 +151,23 @@ func CreateTestGithubCredentials(ctx context.Context, credsName string, db commo return newCreds } +func CreateTestGiteaCredentials(ctx context.Context, credsName string, db common.Store, s *testing.T, endpoint params.ForgeEndpoint) params.ForgeCredentials { + newCredsParams := params.CreateGiteaCredentialsParams{ + Name: credsName, + Description: "Test creds", + AuthType: params.ForgeAuthTypePAT, + Endpoint: endpoint.Name, + PAT: params.GithubPAT{ + OAuth2Token: "test-token", + }, + } + newCreds, err := db.CreateGiteaCredentials(ctx, newCredsParams) + if err != nil { + s.Fatalf("failed to create database object (%s): %v", credsName, err) + } + return newCreds +} + func GetTestSqliteDBConfig(t *testing.T) config.Database { dir, err := os.MkdirTemp("", "garm-config-test") if err != nil { diff --git a/runner/enterprises.go b/runner/enterprises.go index 6cbe54d0..1dbb2171 100644 --- a/runner/enterprises.go +++ b/runner/enterprises.go @@ -39,7 +39,7 @@ func (r *Runner) CreateEnterprise(ctx context.Context, param params.CreateEnterp return params.Enterprise{}, runnerErrors.NewConflictError("enterprise %s already exists", param.Name) } - enterprise, err = r.store.CreateEnterprise(ctx, param.Name, creds.Name, param.WebhookSecret, param.PoolBalancerType) + enterprise, err = r.store.CreateEnterprise(ctx, param.Name, creds, param.WebhookSecret, param.PoolBalancerType) if err != nil { return params.Enterprise{}, errors.Wrap(err, "creating enterprise") } diff --git a/runner/enterprises_test.go b/runner/enterprises_test.go index 5c09ae5d..d5eef463 100644 --- a/runner/enterprises_test.go +++ b/runner/enterprises_test.go @@ -81,7 +81,7 @@ func (s *EnterpriseTestSuite) SetupTest() { enterprise, err := db.CreateEnterprise( adminCtx, name, - s.testCreds.Name, + s.testCreds, fmt.Sprintf("test-webhook-secret-%v", i), params.PoolBalancerTypeRoundRobin, ) From 6994c8ce05e5930e3e98fa34e33cff839ff026df Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Tue, 20 May 2025 09:40:15 +0000 Subject: [PATCH 18/21] Add copyright header Signed-off-by: Gabriel Adrian Samfira --- apiserver/controllers/gitea_credentials.go | 13 +++++++++++++ apiserver/controllers/gitea_endpoints.go | 13 +++++++++++++ apiserver/controllers/github_credentials.go | 13 +++++++++++++ apiserver/controllers/github_endpoints.go | 13 +++++++++++++ apiserver/events/events.go | 13 +++++++++++++ apiserver/events/params.go | 13 +++++++++++++ auth/admin_required.go | 13 +++++++++++++ auth/metrics.go | 13 +++++++++++++ cache/cache_test.go | 13 +++++++++++++ cache/credentials_cache.go | 13 +++++++++++++ cache/entity_cache.go | 13 +++++++++++++ cache/github_client.go | 13 +++++++++++++ cache/instance_cache.go | 13 +++++++++++++ cache/tools_cache.go | 13 +++++++++++++ cache/util.go | 13 +++++++++++++ cmd/garm-cli/cmd/events.go | 13 +++++++++++++ cmd/garm-cli/cmd/gitea.go | 13 +++++++++++++ cmd/garm-cli/cmd/gitea_credentials.go | 20 ++++++++++---------- cmd/garm-cli/cmd/gitea_endpoints.go | 13 +++++++++++++ cmd/garm-cli/cmd/github.go | 13 +++++++++++++ cmd/garm-cli/cmd/github_endpoints.go | 13 +++++++++++++ cmd/garm-cli/cmd/log.go | 13 +++++++++++++ cmd/garm-cli/common/cobra.go | 13 +++++++++++++ cmd/garm-cli/config/home_nix.go | 13 +++++++++++++ cmd/garm-cli/config/home_windows.go | 13 +++++++++++++ database/common/errors.go | 14 ++++++++++++++ database/common/watcher.go | 14 ++++++++++++++ database/sql/common_test.go | 14 ++++++++++++++ database/sql/gitea.go | 14 ++++++++++++++ database/sql/gitea_test.go | 10 +++++----- database/sql/jobs.go | 14 ++++++++++++++ database/sql/models.go | 14 ++++++++++++++ database/sql/scaleset_instances.go | 14 ++++++++++++++ database/sql/scalesets.go | 20 ++++++++++---------- database/sql/scalesets_test.go | 14 ++++++++++++++ database/watcher/consumer.go | 14 ++++++++++++++ database/watcher/filters.go | 14 ++++++++++++++ database/watcher/producer.go | 14 ++++++++++++++ database/watcher/test_export.go | 13 +++++++++++++ database/watcher/watcher.go | 14 ++++++++++++++ database/watcher/watcher_store_test.go | 14 ++++++++++++++ database/watcher/watcher_test.go | 13 +++++++++++++ internal/testing/mock_watcher.go | 14 ++++++++++++++ locking/interface.go | 14 ++++++++++++++ locking/local_backoff_locker.go | 14 ++++++++++++++ locking/local_backoff_locker_test.go | 14 ++++++++++++++ locking/local_locker.go | 14 ++++++++++++++ locking/local_locker_test.go | 13 +++++++++++++ locking/locking.go | 14 ++++++++++++++ metrics/enterprise.go | 14 ++++++++++++++ metrics/github.go | 14 ++++++++++++++ metrics/health.go | 14 ++++++++++++++ metrics/instance.go | 14 ++++++++++++++ metrics/metrics.go | 14 ++++++++++++++ metrics/organization.go | 14 ++++++++++++++ metrics/pool.go | 14 ++++++++++++++ metrics/provider.go | 14 ++++++++++++++ metrics/repository.go | 14 ++++++++++++++ metrics/util.go | 14 ++++++++++++++ metrics/webhooks.go | 14 ++++++++++++++ params/interfaces.go | 14 ++++++++++++++ runner/common/util.go | 14 ++++++++++++++ runner/common_test.go | 14 ++++++++++++++ runner/enterprises.go | 14 ++++++++++++++ runner/gitea_credentials.go | 14 ++++++++++++++ runner/gitea_endpoints.go | 14 ++++++++++++++ runner/github_credentials.go | 14 ++++++++++++++ runner/github_endpoints.go | 14 ++++++++++++++ runner/metadata.go | 14 ++++++++++++++ runner/metrics/enterprise.go | 14 ++++++++++++++ runner/metrics/health.go | 14 ++++++++++++++ runner/metrics/instance.go | 14 ++++++++++++++ runner/metrics/metrics.go | 14 ++++++++++++++ runner/metrics/organization.go | 14 ++++++++++++++ runner/metrics/pool.go | 14 ++++++++++++++ runner/metrics/provider.go | 14 ++++++++++++++ runner/metrics/repository.go | 14 ++++++++++++++ runner/pool/common.go | 14 ++++++++++++++ runner/pool/stub_client.go | 14 ++++++++++++++ runner/pool/util.go | 14 ++++++++++++++ runner/pool/util_test.go | 14 ++++++++++++++ runner/pool/watcher.go | 14 ++++++++++++++ runner/providers/common/common.go | 14 ++++++++++++++ runner/providers/external/external.go | 14 ++++++++++++++ runner/providers/util/util.go | 14 ++++++++++++++ runner/providers/v0.1.0/external.go | 14 ++++++++++++++ runner/providers/v0.1.1/external.go | 14 ++++++++++++++ test/integration/client_utils.go | 14 ++++++++++++++ test/integration/credentials_test.go | 13 +++++++++++++ test/integration/endpoints.go | 14 ++++++++++++++ test/integration/endpoints_test.go | 14 ++++++++++++++ test/integration/external_provider_test.go | 13 +++++++++++++ test/integration/gh_cleanup/main.go | 14 ++++++++++++++ test/integration/jobs_test.go | 13 +++++++++++++ test/integration/list_info_test.go | 13 +++++++++++++ test/integration/organizations_test.go | 13 +++++++++++++ test/integration/repositories_test.go | 13 +++++++++++++ test/integration/suite_test.go | 13 +++++++++++++ test/integration/utils.go | 13 +++++++++++++ util/appdefaults/appdefaults.go | 13 +++++++++++++ util/github/gitea.go | 14 ++++++++++++++ util/logging.go | 14 ++++++++++++++ websocket/client.go | 14 ++++++++++++++ websocket/websocket.go | 13 +++++++++++++ workers/cache/cache.go | 13 +++++++++++++ workers/cache/gitea_tools.go | 14 ++++++++++++++ workers/cache/tool_cache.go | 14 ++++++++++++++ workers/common/interfaces.go | 14 ++++++++++++++ workers/entity/controller.go | 13 +++++++++++++ workers/entity/controller_watcher.go | 13 +++++++++++++ workers/entity/util.go | 13 +++++++++++++ workers/entity/worker.go | 13 +++++++++++++ workers/entity/worker_watcher.go | 13 +++++++++++++ workers/provider/errors.go | 13 +++++++++++++ workers/provider/instance_manager.go | 13 +++++++++++++ workers/provider/provider.go | 13 +++++++++++++ workers/provider/provider_helper.go | 13 +++++++++++++ workers/provider/util.go | 13 +++++++++++++ workers/scaleset/controller.go | 13 +++++++++++++ workers/scaleset/controller_watcher.go | 13 +++++++++++++ workers/scaleset/interfaces.go | 13 +++++++++++++ workers/scaleset/scaleset.go | 13 +++++++++++++ workers/scaleset/scaleset_helper.go | 13 +++++++++++++ workers/scaleset/scaleset_listener.go | 13 +++++++++++++ workers/scaleset/util.go | 13 +++++++++++++ 125 files changed, 1678 insertions(+), 25 deletions(-) diff --git a/apiserver/controllers/gitea_credentials.go b/apiserver/controllers/gitea_credentials.go index e1be0fb7..777be982 100644 --- a/apiserver/controllers/gitea_credentials.go +++ b/apiserver/controllers/gitea_credentials.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 controllers import ( diff --git a/apiserver/controllers/gitea_endpoints.go b/apiserver/controllers/gitea_endpoints.go index 6f1525d5..67e85178 100644 --- a/apiserver/controllers/gitea_endpoints.go +++ b/apiserver/controllers/gitea_endpoints.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 controllers import ( diff --git a/apiserver/controllers/github_credentials.go b/apiserver/controllers/github_credentials.go index c7544357..04e087e5 100644 --- a/apiserver/controllers/github_credentials.go +++ b/apiserver/controllers/github_credentials.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 controllers import ( diff --git a/apiserver/controllers/github_endpoints.go b/apiserver/controllers/github_endpoints.go index 491c5716..482f9d03 100644 --- a/apiserver/controllers/github_endpoints.go +++ b/apiserver/controllers/github_endpoints.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 controllers import ( diff --git a/apiserver/events/events.go b/apiserver/events/events.go index 30e0b386..94d707f2 100644 --- a/apiserver/events/events.go +++ b/apiserver/events/events.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 events import ( diff --git a/apiserver/events/params.go b/apiserver/events/params.go index 49bf47fa..a2b996a9 100644 --- a/apiserver/events/params.go +++ b/apiserver/events/params.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 events import ( diff --git a/auth/admin_required.go b/auth/admin_required.go index 8ab6cbac..b3ca3624 100644 --- a/auth/admin_required.go +++ b/auth/admin_required.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 auth import "net/http" diff --git a/auth/metrics.go b/auth/metrics.go index 55cede44..5ea688e2 100644 --- a/auth/metrics.go +++ b/auth/metrics.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 auth import ( diff --git a/cache/cache_test.go b/cache/cache_test.go index 4b7cf332..7a8ebed3 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cache import ( diff --git a/cache/credentials_cache.go b/cache/credentials_cache.go index 6dbef775..3cb5c71d 100644 --- a/cache/credentials_cache.go +++ b/cache/credentials_cache.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cache import ( diff --git a/cache/entity_cache.go b/cache/entity_cache.go index bbbc385d..4800dd9c 100644 --- a/cache/entity_cache.go +++ b/cache/entity_cache.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cache import ( diff --git a/cache/github_client.go b/cache/github_client.go index 0126e45b..179a9718 100644 --- a/cache/github_client.go +++ b/cache/github_client.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cache import ( diff --git a/cache/instance_cache.go b/cache/instance_cache.go index b96db5e9..baf09945 100644 --- a/cache/instance_cache.go +++ b/cache/instance_cache.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cache import ( diff --git a/cache/tools_cache.go b/cache/tools_cache.go index 6e3c4636..30e83a0e 100644 --- a/cache/tools_cache.go +++ b/cache/tools_cache.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cache import ( diff --git a/cache/util.go b/cache/util.go index f8769c65..5fd234a9 100644 --- a/cache/util.go +++ b/cache/util.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cache import ( diff --git a/cmd/garm-cli/cmd/events.go b/cmd/garm-cli/cmd/events.go index f38e9ea6..da44732a 100644 --- a/cmd/garm-cli/cmd/events.go +++ b/cmd/garm-cli/cmd/events.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cmd import ( diff --git a/cmd/garm-cli/cmd/gitea.go b/cmd/garm-cli/cmd/gitea.go index 10d086bd..6627fd6f 100644 --- a/cmd/garm-cli/cmd/gitea.go +++ b/cmd/garm-cli/cmd/gitea.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cmd import "github.com/spf13/cobra" diff --git a/cmd/garm-cli/cmd/gitea_credentials.go b/cmd/garm-cli/cmd/gitea_credentials.go index c744c8a7..d26f95ed 100644 --- a/cmd/garm-cli/cmd/gitea_credentials.go +++ b/cmd/garm-cli/cmd/gitea_credentials.go @@ -1,16 +1,16 @@ -// Copyright 2022 Cloudbase Solutions SRL +// Copyright 2025 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 +// 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 +// 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. +// 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 cmd diff --git a/cmd/garm-cli/cmd/gitea_endpoints.go b/cmd/garm-cli/cmd/gitea_endpoints.go index d3504f17..55fa09c9 100644 --- a/cmd/garm-cli/cmd/gitea_endpoints.go +++ b/cmd/garm-cli/cmd/gitea_endpoints.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cmd import ( diff --git a/cmd/garm-cli/cmd/github.go b/cmd/garm-cli/cmd/github.go index 8b79a381..71342026 100644 --- a/cmd/garm-cli/cmd/github.go +++ b/cmd/garm-cli/cmd/github.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cmd import "github.com/spf13/cobra" diff --git a/cmd/garm-cli/cmd/github_endpoints.go b/cmd/garm-cli/cmd/github_endpoints.go index fbdca86c..61f46810 100644 --- a/cmd/garm-cli/cmd/github_endpoints.go +++ b/cmd/garm-cli/cmd/github_endpoints.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cmd import ( diff --git a/cmd/garm-cli/cmd/log.go b/cmd/garm-cli/cmd/log.go index 901e8e0f..e930ae69 100644 --- a/cmd/garm-cli/cmd/log.go +++ b/cmd/garm-cli/cmd/log.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cmd import ( diff --git a/cmd/garm-cli/common/cobra.go b/cmd/garm-cli/common/cobra.go index e59a2aca..399a4b92 100644 --- a/cmd/garm-cli/common/cobra.go +++ b/cmd/garm-cli/common/cobra.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 common import "fmt" diff --git a/cmd/garm-cli/config/home_nix.go b/cmd/garm-cli/config/home_nix.go index 27aed4f8..e9ffa521 100644 --- a/cmd/garm-cli/config/home_nix.go +++ b/cmd/garm-cli/config/home_nix.go @@ -1,6 +1,19 @@ //go:build !windows // +build !windows +// Copyright 2025 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 config import ( diff --git a/cmd/garm-cli/config/home_windows.go b/cmd/garm-cli/config/home_windows.go index d34379b4..c70fb645 100644 --- a/cmd/garm-cli/config/home_windows.go +++ b/cmd/garm-cli/config/home_windows.go @@ -1,6 +1,19 @@ //go:build windows && !linux // +build windows,!linux +// Copyright 2025 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 config import ( diff --git a/database/common/errors.go b/database/common/errors.go index df2d936a..5e6a5087 100644 --- a/database/common/errors.go +++ b/database/common/errors.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 common import "fmt" diff --git a/database/common/watcher.go b/database/common/watcher.go index 4dc18437..94152094 100644 --- a/database/common/watcher.go +++ b/database/common/watcher.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 common import "context" diff --git a/database/sql/common_test.go b/database/sql/common_test.go index af0adcf9..a3c62e06 100644 --- a/database/sql/common_test.go +++ b/database/sql/common_test.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 const ( diff --git a/database/sql/gitea.go b/database/sql/gitea.go index 79680287..3b4c55ec 100644 --- a/database/sql/gitea.go +++ b/database/sql/gitea.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/database/sql/gitea_test.go b/database/sql/gitea_test.go index c7a96d66..8eaaf23b 100644 --- a/database/sql/gitea_test.go +++ b/database/sql/gitea_test.go @@ -161,7 +161,7 @@ func (s *GiteaTestSuite) TestUpdateEndpoint() { s.Require().NoError(err) s.Require().NotNil(endpoint) - newDescription := "new description" + newDescription := "another description" newAPIBaseURL := "https://new-api.example.com" newBaseURL := "https://new.example.com" caCertBundle, err := os.ReadFile("../../testdata/certs/srv-pub.pem") @@ -185,7 +185,7 @@ func (s *GiteaTestSuite) TestUpdateEndpoint() { func (s *GiteaTestSuite) TestUpdatingNonExistingEndpointReturnsNotFoundError() { ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) - newDescription := "test" + newDescription := "test desc" updateEpParams := params.UpdateGiteaEndpointParams{ Description: &newDescription, } @@ -573,7 +573,7 @@ func (s *GiteaTestSuite) TestUpdateCredentials() { s.Require().NoError(err) s.Require().NotNil(creds) - newDescription := "new description" + newDescription := "just a description" newName := "new-name" newToken := "new-token" updateCredParams := params.UpdateGiteaCredentialsParams{ @@ -622,7 +622,7 @@ func (s *GiteaTestSuite) TestUpdateCredentialsFailsIfCredentialsAreOwnedByNonAdm s.Require().NoError(err) s.Require().NotNil(creds) - newDescription := "new description2" + newDescription := "new params desc" updateCredParams := params.UpdateGiteaCredentialsParams{ Description: &newDescription, } @@ -651,7 +651,7 @@ func (s *GiteaTestSuite) TestAdminUserCanUpdateAnyGiteaCredentials() { s.Require().NoError(err) s.Require().NotNil(creds) - newDescription := "new description2" + newDescription := "another new description" updateCredParams := params.UpdateGiteaCredentialsParams{ Description: &newDescription, } diff --git a/database/sql/jobs.go b/database/sql/jobs.go index 7f9b7b00..1215e3f3 100644 --- a/database/sql/jobs.go +++ b/database/sql/jobs.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/database/sql/models.go b/database/sql/models.go index e7fad261..154fb51d 100644 --- a/database/sql/models.go +++ b/database/sql/models.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/database/sql/scaleset_instances.go b/database/sql/scaleset_instances.go index fcb9e1f2..bbc4f593 100644 --- a/database/sql/scaleset_instances.go +++ b/database/sql/scaleset_instances.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/database/sql/scalesets.go b/database/sql/scalesets.go index 1d272704..930ae17d 100644 --- a/database/sql/scalesets.go +++ b/database/sql/scalesets.go @@ -1,16 +1,16 @@ -// Copyright 2024 Cloudbase Solutions SRL +// Copyright 2025 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 +// 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 +// 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. +// 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 diff --git a/database/sql/scalesets_test.go b/database/sql/scalesets_test.go index 7d49b397..f1f9fbba 100644 --- a/database/sql/scalesets_test.go +++ b/database/sql/scalesets_test.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/database/watcher/consumer.go b/database/watcher/consumer.go index 9282ece8..ed0967e9 100644 --- a/database/watcher/consumer.go +++ b/database/watcher/consumer.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 watcher import ( diff --git a/database/watcher/filters.go b/database/watcher/filters.go index 7f7a7ab9..b09c422d 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 watcher import ( diff --git a/database/watcher/producer.go b/database/watcher/producer.go index 159ad843..927aada0 100644 --- a/database/watcher/producer.go +++ b/database/watcher/producer.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 watcher import ( diff --git a/database/watcher/test_export.go b/database/watcher/test_export.go index f9b4ecf1..eb3d38b6 100644 --- a/database/watcher/test_export.go +++ b/database/watcher/test_export.go @@ -1,6 +1,19 @@ //go:build testing // +build testing +// Copyright 2025 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 watcher import "github.com/cloudbase/garm/database/common" diff --git a/database/watcher/watcher.go b/database/watcher/watcher.go index fda318c6..a7e1cd67 100644 --- a/database/watcher/watcher.go +++ b/database/watcher/watcher.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 watcher import ( diff --git a/database/watcher/watcher_store_test.go b/database/watcher/watcher_store_test.go index 7045455e..8fe7d0a4 100644 --- a/database/watcher/watcher_store_test.go +++ b/database/watcher/watcher_store_test.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 watcher_test import ( diff --git a/database/watcher/watcher_test.go b/database/watcher/watcher_test.go index c5b56fe2..b272bda7 100644 --- a/database/watcher/watcher_test.go +++ b/database/watcher/watcher_test.go @@ -1,5 +1,18 @@ //go:build testing +// Copyright 2025 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 watcher_test import ( diff --git a/internal/testing/mock_watcher.go b/internal/testing/mock_watcher.go index 67ae5da4..112f0de5 100644 --- a/internal/testing/mock_watcher.go +++ b/internal/testing/mock_watcher.go @@ -1,6 +1,20 @@ //go:build testing // +build testing +// Copyright 2025 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 testing import ( diff --git a/locking/interface.go b/locking/interface.go index 2b6ffb47..43ed1737 100644 --- a/locking/interface.go +++ b/locking/interface.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 locking import "time" diff --git a/locking/local_backoff_locker.go b/locking/local_backoff_locker.go index 9c2fecb1..93344566 100644 --- a/locking/local_backoff_locker.go +++ b/locking/local_backoff_locker.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 locking import ( diff --git a/locking/local_backoff_locker_test.go b/locking/local_backoff_locker_test.go index a9a986e2..00fe09c8 100644 --- a/locking/local_backoff_locker_test.go +++ b/locking/local_backoff_locker_test.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 locking import ( diff --git a/locking/local_locker.go b/locking/local_locker.go index fc5ea847..312d85ec 100644 --- a/locking/local_locker.go +++ b/locking/local_locker.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 locking import ( diff --git a/locking/local_locker_test.go b/locking/local_locker_test.go index 6decf512..75b4dac0 100644 --- a/locking/local_locker_test.go +++ b/locking/local_locker_test.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 locking import ( diff --git a/locking/locking.go b/locking/locking.go index d485f5ff..312d2e6a 100644 --- a/locking/locking.go +++ b/locking/locking.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 locking import ( diff --git a/metrics/enterprise.go b/metrics/enterprise.go index f8382edf..882b64df 100644 --- a/metrics/enterprise.go +++ b/metrics/enterprise.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/metrics/github.go b/metrics/github.go index 0c050652..0d6f5fa7 100644 --- a/metrics/github.go +++ b/metrics/github.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import "github.com/prometheus/client_golang/prometheus" diff --git a/metrics/health.go b/metrics/health.go index 4acfbb36..13194231 100644 --- a/metrics/health.go +++ b/metrics/health.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/metrics/instance.go b/metrics/instance.go index 7c2f2f96..b9d7e1cf 100644 --- a/metrics/instance.go +++ b/metrics/instance.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/metrics/metrics.go b/metrics/metrics.go index edceb30a..1a566116 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/metrics/organization.go b/metrics/organization.go index 38d7c611..d04e7a4e 100644 --- a/metrics/organization.go +++ b/metrics/organization.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/metrics/pool.go b/metrics/pool.go index 5803af90..fc6f2520 100644 --- a/metrics/pool.go +++ b/metrics/pool.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/metrics/provider.go b/metrics/provider.go index 8285ca1e..3262ab3b 100644 --- a/metrics/provider.go +++ b/metrics/provider.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/metrics/repository.go b/metrics/repository.go index a84dd120..21714233 100644 --- a/metrics/repository.go +++ b/metrics/repository.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/metrics/util.go b/metrics/util.go index b2edb580..d83b4973 100644 --- a/metrics/util.go +++ b/metrics/util.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics func Bool2float64(b bool) float64 { diff --git a/metrics/webhooks.go b/metrics/webhooks.go index 839219a4..48a08f9c 100644 --- a/metrics/webhooks.go +++ b/metrics/webhooks.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import "github.com/prometheus/client_golang/prometheus" diff --git a/params/interfaces.go b/params/interfaces.go index ccf04e06..31ef635f 100644 --- a/params/interfaces.go +++ b/params/interfaces.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 params import "time" diff --git a/runner/common/util.go b/runner/common/util.go index 39c03651..2720c496 100644 --- a/runner/common/util.go +++ b/runner/common/util.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 common import ( diff --git a/runner/common_test.go b/runner/common_test.go index b9b53545..247b5ab1 100644 --- a/runner/common_test.go +++ b/runner/common_test.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 runner const ( diff --git a/runner/enterprises.go b/runner/enterprises.go index 1dbb2171..f192c7cd 100644 --- a/runner/enterprises.go +++ b/runner/enterprises.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 runner import ( diff --git a/runner/gitea_credentials.go b/runner/gitea_credentials.go index 749f2346..4fdad1d2 100644 --- a/runner/gitea_credentials.go +++ b/runner/gitea_credentials.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 runner import ( diff --git a/runner/gitea_endpoints.go b/runner/gitea_endpoints.go index 847dbab9..181f8e7e 100644 --- a/runner/gitea_endpoints.go +++ b/runner/gitea_endpoints.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 runner import ( diff --git a/runner/github_credentials.go b/runner/github_credentials.go index 7c368c99..ec524056 100644 --- a/runner/github_credentials.go +++ b/runner/github_credentials.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 runner import ( diff --git a/runner/github_endpoints.go b/runner/github_endpoints.go index 3f4fb308..0e144447 100644 --- a/runner/github_endpoints.go +++ b/runner/github_endpoints.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 runner import ( diff --git a/runner/metadata.go b/runner/metadata.go index 3df7966a..2c917ea0 100644 --- a/runner/metadata.go +++ b/runner/metadata.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 runner import ( diff --git a/runner/metrics/enterprise.go b/runner/metrics/enterprise.go index 407c0fc4..3ab9003c 100644 --- a/runner/metrics/enterprise.go +++ b/runner/metrics/enterprise.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/runner/metrics/health.go b/runner/metrics/health.go index 05e1ed9b..fcd254df 100644 --- a/runner/metrics/health.go +++ b/runner/metrics/health.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/runner/metrics/instance.go b/runner/metrics/instance.go index 06fd4881..bc6bed0a 100644 --- a/runner/metrics/instance.go +++ b/runner/metrics/instance.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/runner/metrics/metrics.go b/runner/metrics/metrics.go index f9f70864..772ba86a 100644 --- a/runner/metrics/metrics.go +++ b/runner/metrics/metrics.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/runner/metrics/organization.go b/runner/metrics/organization.go index 6b9f6b71..3716cca1 100644 --- a/runner/metrics/organization.go +++ b/runner/metrics/organization.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/runner/metrics/pool.go b/runner/metrics/pool.go index 44ad27a8..6b06a8b9 100644 --- a/runner/metrics/pool.go +++ b/runner/metrics/pool.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/runner/metrics/provider.go b/runner/metrics/provider.go index e2b38a9f..1d7a065d 100644 --- a/runner/metrics/provider.go +++ b/runner/metrics/provider.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/runner/metrics/repository.go b/runner/metrics/repository.go index b76fcc0e..36e07bf0 100644 --- a/runner/metrics/repository.go +++ b/runner/metrics/repository.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 metrics import ( diff --git a/runner/pool/common.go b/runner/pool/common.go index 6820be1a..5316e07e 100644 --- a/runner/pool/common.go +++ b/runner/pool/common.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/runner/pool/stub_client.go b/runner/pool/stub_client.go index e8f1e7c6..6fa44e74 100644 --- a/runner/pool/stub_client.go +++ b/runner/pool/stub_client.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/runner/pool/util.go b/runner/pool/util.go index 4c4bf5b1..dd55e1db 100644 --- a/runner/pool/util.go +++ b/runner/pool/util.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/runner/pool/util_test.go b/runner/pool/util_test.go index bcfea879..67d31f76 100644 --- a/runner/pool/util_test.go +++ b/runner/pool/util_test.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/runner/pool/watcher.go b/runner/pool/watcher.go index 97089133..324643ce 100644 --- a/runner/pool/watcher.go +++ b/runner/pool/watcher.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 ( diff --git a/runner/providers/common/common.go b/runner/providers/common/common.go index 4e49e080..f1a5a66d 100644 --- a/runner/providers/common/common.go +++ b/runner/providers/common/common.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 common import ( diff --git a/runner/providers/external/external.go b/runner/providers/external/external.go index 23b9b894..46e3dd47 100644 --- a/runner/providers/external/external.go +++ b/runner/providers/external/external.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 external import ( diff --git a/runner/providers/util/util.go b/runner/providers/util/util.go index 2948730b..fb3c12bd 100644 --- a/runner/providers/util/util.go +++ b/runner/providers/util/util.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 util import ( diff --git a/runner/providers/v0.1.0/external.go b/runner/providers/v0.1.0/external.go index 6dd0ef46..60c5ca1b 100644 --- a/runner/providers/v0.1.0/external.go +++ b/runner/providers/v0.1.0/external.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 v010 import ( diff --git a/runner/providers/v0.1.1/external.go b/runner/providers/v0.1.1/external.go index 530a2645..192f735d 100644 --- a/runner/providers/v0.1.1/external.go +++ b/runner/providers/v0.1.1/external.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 v011 import ( diff --git a/test/integration/client_utils.go b/test/integration/client_utils.go index c986be2d..e423c107 100644 --- a/test/integration/client_utils.go +++ b/test/integration/client_utils.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 integration import ( diff --git a/test/integration/credentials_test.go b/test/integration/credentials_test.go index f1594f87..9b9387f6 100644 --- a/test/integration/credentials_test.go +++ b/test/integration/credentials_test.go @@ -1,6 +1,19 @@ //go:build integration // +build integration +// Copyright 2025 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 integration import ( diff --git a/test/integration/endpoints.go b/test/integration/endpoints.go index 9f1320ca..720f43d2 100644 --- a/test/integration/endpoints.go +++ b/test/integration/endpoints.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 integration import ( diff --git a/test/integration/endpoints_test.go b/test/integration/endpoints_test.go index a958259f..fe0dd160 100644 --- a/test/integration/endpoints_test.go +++ b/test/integration/endpoints_test.go @@ -1,6 +1,20 @@ //go:build integration // +build integration +// Copyright 2025 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 integration import ( diff --git a/test/integration/external_provider_test.go b/test/integration/external_provider_test.go index ceb5b162..2c85eb35 100644 --- a/test/integration/external_provider_test.go +++ b/test/integration/external_provider_test.go @@ -1,6 +1,19 @@ //go:build integration // +build integration +// Copyright 2025 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 integration import ( diff --git a/test/integration/gh_cleanup/main.go b/test/integration/gh_cleanup/main.go index 6ec50304..95f1aa78 100644 --- a/test/integration/gh_cleanup/main.go +++ b/test/integration/gh_cleanup/main.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 main import ( diff --git a/test/integration/jobs_test.go b/test/integration/jobs_test.go index 4d87c077..8da94414 100644 --- a/test/integration/jobs_test.go +++ b/test/integration/jobs_test.go @@ -1,6 +1,19 @@ //go:build integration // +build integration +// Copyright 2025 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 integration import ( diff --git a/test/integration/list_info_test.go b/test/integration/list_info_test.go index 1eef816e..ddb3ff86 100644 --- a/test/integration/list_info_test.go +++ b/test/integration/list_info_test.go @@ -1,6 +1,19 @@ //go:build integration // +build integration +// Copyright 2025 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 integration import ( diff --git a/test/integration/organizations_test.go b/test/integration/organizations_test.go index a96e625c..8acfb6d3 100644 --- a/test/integration/organizations_test.go +++ b/test/integration/organizations_test.go @@ -1,6 +1,19 @@ //go:build integration // +build integration +// Copyright 2025 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 integration import ( diff --git a/test/integration/repositories_test.go b/test/integration/repositories_test.go index 1f111fcc..43a5d8ec 100644 --- a/test/integration/repositories_test.go +++ b/test/integration/repositories_test.go @@ -1,6 +1,19 @@ //go:build integration // +build integration +// Copyright 2025 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 integration import ( diff --git a/test/integration/suite_test.go b/test/integration/suite_test.go index c2f4bd5f..ca6b3030 100644 --- a/test/integration/suite_test.go +++ b/test/integration/suite_test.go @@ -1,6 +1,19 @@ //go:build integration // +build integration +// Copyright 2025 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 integration import ( diff --git a/test/integration/utils.go b/test/integration/utils.go index 24e97b7f..1fa35b5e 100644 --- a/test/integration/utils.go +++ b/test/integration/utils.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 integration import ( diff --git a/util/appdefaults/appdefaults.go b/util/appdefaults/appdefaults.go index 479db08f..cc53f794 100644 --- a/util/appdefaults/appdefaults.go +++ b/util/appdefaults/appdefaults.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 appdefaults import "time" diff --git a/util/github/gitea.go b/util/github/gitea.go index 0359836e..e657db48 100644 --- a/util/github/gitea.go +++ b/util/github/gitea.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 github import ( diff --git a/util/logging.go b/util/logging.go index bb7b0562..4c37ed34 100644 --- a/util/logging.go +++ b/util/logging.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 util import ( diff --git a/websocket/client.go b/websocket/client.go index 657aa49e..70777265 100644 --- a/websocket/client.go +++ b/websocket/client.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 websocket import ( diff --git a/websocket/websocket.go b/websocket/websocket.go index 14b5e785..e59eb70e 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 websocket import ( diff --git a/workers/cache/cache.go b/workers/cache/cache.go index 8cc14224..8f53cb67 100644 --- a/workers/cache/cache.go +++ b/workers/cache/cache.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 cache import ( diff --git a/workers/cache/gitea_tools.go b/workers/cache/gitea_tools.go index 4474f7df..5d09ccb3 100644 --- a/workers/cache/gitea_tools.go +++ b/workers/cache/gitea_tools.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 cache import ( diff --git a/workers/cache/tool_cache.go b/workers/cache/tool_cache.go index 941131d7..2e91bf50 100644 --- a/workers/cache/tool_cache.go +++ b/workers/cache/tool_cache.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 cache import ( diff --git a/workers/common/interfaces.go b/workers/common/interfaces.go index 4791a500..a04f16a6 100644 --- a/workers/common/interfaces.go +++ b/workers/common/interfaces.go @@ -1,3 +1,17 @@ +// Copyright 2025 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 common import ( diff --git a/workers/entity/controller.go b/workers/entity/controller.go index 2cb910b3..99618194 100644 --- a/workers/entity/controller.go +++ b/workers/entity/controller.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 entity import ( diff --git a/workers/entity/controller_watcher.go b/workers/entity/controller_watcher.go index ace63702..6bd3e173 100644 --- a/workers/entity/controller_watcher.go +++ b/workers/entity/controller_watcher.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 entity import ( diff --git a/workers/entity/util.go b/workers/entity/util.go index 877758a7..2216c326 100644 --- a/workers/entity/util.go +++ b/workers/entity/util.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 entity import ( diff --git a/workers/entity/worker.go b/workers/entity/worker.go index efb40ac1..583ab2c8 100644 --- a/workers/entity/worker.go +++ b/workers/entity/worker.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 entity import ( diff --git a/workers/entity/worker_watcher.go b/workers/entity/worker_watcher.go index 273a8f02..9acfbc60 100644 --- a/workers/entity/worker_watcher.go +++ b/workers/entity/worker_watcher.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 entity import ( diff --git a/workers/provider/errors.go b/workers/provider/errors.go index 40cfc9a8..7c9247dc 100644 --- a/workers/provider/errors.go +++ b/workers/provider/errors.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 provider import "fmt" diff --git a/workers/provider/instance_manager.go b/workers/provider/instance_manager.go index 3900abaf..84e5bcca 100644 --- a/workers/provider/instance_manager.go +++ b/workers/provider/instance_manager.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 provider import ( diff --git a/workers/provider/provider.go b/workers/provider/provider.go index b1ab1220..78e50955 100644 --- a/workers/provider/provider.go +++ b/workers/provider/provider.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 provider import ( diff --git a/workers/provider/provider_helper.go b/workers/provider/provider_helper.go index 96762135..13694794 100644 --- a/workers/provider/provider_helper.go +++ b/workers/provider/provider_helper.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 provider import ( diff --git a/workers/provider/util.go b/workers/provider/util.go index 8cd33525..cf27d14f 100644 --- a/workers/provider/util.go +++ b/workers/provider/util.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 provider import ( diff --git a/workers/scaleset/controller.go b/workers/scaleset/controller.go index 4aba42c2..63112f43 100644 --- a/workers/scaleset/controller.go +++ b/workers/scaleset/controller.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 scaleset import ( diff --git a/workers/scaleset/controller_watcher.go b/workers/scaleset/controller_watcher.go index 551d711d..8344cac5 100644 --- a/workers/scaleset/controller_watcher.go +++ b/workers/scaleset/controller_watcher.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 scaleset import ( diff --git a/workers/scaleset/interfaces.go b/workers/scaleset/interfaces.go index ca529ce1..b8acfceb 100644 --- a/workers/scaleset/interfaces.go +++ b/workers/scaleset/interfaces.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 scaleset import ( diff --git a/workers/scaleset/scaleset.go b/workers/scaleset/scaleset.go index b3bfe332..e4faba70 100644 --- a/workers/scaleset/scaleset.go +++ b/workers/scaleset/scaleset.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 scaleset import ( diff --git a/workers/scaleset/scaleset_helper.go b/workers/scaleset/scaleset_helper.go index 54d9b52e..7b3fdf03 100644 --- a/workers/scaleset/scaleset_helper.go +++ b/workers/scaleset/scaleset_helper.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 scaleset import ( diff --git a/workers/scaleset/scaleset_listener.go b/workers/scaleset/scaleset_listener.go index 7e0ec869..76a321f4 100644 --- a/workers/scaleset/scaleset_listener.go +++ b/workers/scaleset/scaleset_listener.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 scaleset import ( diff --git a/workers/scaleset/util.go b/workers/scaleset/util.go index 1d8d6c51..7852cb89 100644 --- a/workers/scaleset/util.go +++ b/workers/scaleset/util.go @@ -1,3 +1,16 @@ +// Copyright 2025 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 scaleset import ( From b4268e6bab4e6335f7057a948d6f248fa36f01a5 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Tue, 20 May 2025 09:52:13 +0000 Subject: [PATCH 19/21] Fix tests Signed-off-by: Gabriel Adrian Samfira --- database/sql/enterprise_test.go | 8 -------- database/sql/gitea_test.go | 18 +++++++++--------- database/sql/github_test.go | 15 ++++++++------- database/sql/repositories_test.go | 2 +- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/database/sql/enterprise_test.go b/database/sql/enterprise_test.go index 9d81287d..24e7bee7 100644 --- a/database/sql/enterprise_test.go +++ b/database/sql/enterprise_test.go @@ -219,14 +219,6 @@ func (s *EnterpriseTestSuite) TestCreateEnterpriseInvalidDBPassphrase() { func (s *EnterpriseTestSuite) TestCreateEnterpriseDBCreateErr() { s.Fixtures.SQLMock.ExpectBegin() - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE user_id = ? AND name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). - WithArgs(s.adminUserID, s.Fixtures.Enterprises[0].CredentialsName, 1). - WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}).AddRow(s.testCreds.ID, s.testCreds.Endpoint.Name)) - s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). - WithArgs(s.testCreds.Endpoint.Name). - WillReturnRows(sqlmock.NewRows([]string{"name"}). - AddRow(s.testCreds.Endpoint.Name)) s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `enterprises`")). WillReturnError(fmt.Errorf("creating enterprise mock error")) diff --git a/database/sql/gitea_test.go b/database/sql/gitea_test.go index 8eaaf23b..a70d3b1f 100644 --- a/database/sql/gitea_test.go +++ b/database/sql/gitea_test.go @@ -61,7 +61,7 @@ func (s *GiteaTestSuite) TestCreatingEndpoint() { ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) createEpParams := params.CreateGiteaEndpointParams{ - Name: testEndpointName, + Name: alternetTestEndpointName, Description: testEndpointDescription, APIBaseURL: testAPIBaseURL, BaseURL: testBaseURL, @@ -70,14 +70,14 @@ func (s *GiteaTestSuite) TestCreatingEndpoint() { endpoint, err := s.db.CreateGiteaEndpoint(ctx, createEpParams) s.Require().NoError(err) s.Require().NotNil(endpoint) - s.Require().Equal(testEndpointName, endpoint.Name) + s.Require().Equal(alternetTestEndpointName, endpoint.Name) } func (s *GiteaTestSuite) TestCreatingDuplicateEndpointFails() { ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) createEpParams := params.CreateGiteaEndpointParams{ - Name: testEndpointName, + Name: alternetTestEndpointName, Description: testEndpointDescription, APIBaseURL: testAPIBaseURL, BaseURL: testBaseURL, @@ -95,7 +95,7 @@ func (s *GiteaTestSuite) TestGetEndpoint() { ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) createEpParams := params.CreateGiteaEndpointParams{ - Name: "deleteme", + Name: alternetTestEndpointName, Description: testEndpointDescription, APIBaseURL: testAPIBaseURL, BaseURL: testBaseURL, @@ -129,7 +129,7 @@ func (s *GiteaTestSuite) TestDeletingEndpoint() { ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) createEpParams := params.CreateGiteaEndpointParams{ - Name: testEndpointName, + Name: alternetTestEndpointName, Description: testEndpointDescription, APIBaseURL: testAPIBaseURL, BaseURL: testBaseURL, @@ -139,10 +139,10 @@ func (s *GiteaTestSuite) TestDeletingEndpoint() { s.Require().NoError(err) s.Require().NotNil(endpoint) - err = s.db.DeleteGiteaEndpoint(ctx, testEndpointName) + err = s.db.DeleteGiteaEndpoint(ctx, alternetTestEndpointName) s.Require().NoError(err) - _, err = s.db.GetGiteaEndpoint(ctx, testEndpointName) + _, err = s.db.GetGiteaEndpoint(ctx, alternetTestEndpointName) s.Require().Error(err) s.Require().ErrorIs(err, runnerErrors.ErrNotFound) } @@ -199,7 +199,7 @@ func (s *GiteaTestSuite) TestListEndpoints() { ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T()) createEpParams := params.CreateGiteaEndpointParams{ - Name: testEndpointName, + Name: alternetTestEndpointName, Description: testEndpointDescription, APIBaseURL: testAPIBaseURL, BaseURL: testBaseURL, @@ -210,7 +210,7 @@ func (s *GiteaTestSuite) TestListEndpoints() { endpoints, err := s.db.ListGiteaEndpoints(ctx) s.Require().NoError(err) - s.Require().Len(endpoints, 1) + s.Require().Len(endpoints, 2) } func (s *GiteaTestSuite) TestCreateCredentialsFailsWithUnauthorizedForAnonUser() { diff --git a/database/sql/github_test.go b/database/sql/github_test.go index f2e83fec..7b99d5e2 100644 --- a/database/sql/github_test.go +++ b/database/sql/github_test.go @@ -33,13 +33,14 @@ import ( ) const ( - testUploadBaseURL string = "https://uploads.example.com" - testBaseURL string = "https://example.com" - testAPIBaseURL string = "https://api.example.com" - testEndpointName string = "test-endpoint" - testEndpointDescription string = "test description" - testCredsName string = "test-creds" - testCredsDescription string = "test creds" + testUploadBaseURL string = "https://uploads.example.com" + testBaseURL string = "https://example.com" + testAPIBaseURL string = "https://api.example.com" + testEndpointName string = "test-endpoint" + alternetTestEndpointName string = "test-endpoint-alternate" + testEndpointDescription string = "test description" + testCredsName string = "test-creds" + testCredsDescription string = "test creds" ) type GithubTestSuite struct { diff --git a/database/sql/repositories_test.go b/database/sql/repositories_test.go index 3f8d8ca2..f27e10b5 100644 --- a/database/sql/repositories_test.go +++ b/database/sql/repositories_test.go @@ -226,7 +226,7 @@ func (s *RepoTestSuite) TestCreateRepository() { entity, err := repo.GetEntity() s.Require().Nil(err) - s.Require().Equal(s.Fixtures.CreateRepoParams.Owner, entity.ID) + s.Require().Equal(s.Fixtures.CreateRepoParams.Owner, entity.Owner) s.Require().Equal(entity.EntityType, params.ForgeEntityTypeRepository) forgeType, err := entity.GetForgeType() From 1fe09548bca0f770e9d8f09a4ae4ead38bbd1331 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Tue, 20 May 2025 13:50:56 +0000 Subject: [PATCH 20/21] Add more tests Signed-off-by: Gabriel Adrian Samfira --- database/watcher/filters.go | 13 +- database/watcher/util_test.go | 16 + database/watcher/watcher_test.go | 1274 +++++++++++++++++++++++++++++- 3 files changed, 1288 insertions(+), 15 deletions(-) create mode 100644 database/watcher/util_test.go diff --git a/database/watcher/filters.go b/database/watcher/filters.go index b09c422d..acf79ba8 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -205,21 +205,26 @@ func WithEntityJobFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFunc } } -// WithForgeCredentialsFilter returns a filter function that filters payloads by Github credentials. +// WithForgeCredentialsFilter returns a filter function that filters payloads by Github or Gitea credentials. func WithForgeCredentialsFilter(creds params.ForgeCredentials) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { - var idGetter params.IDGetter + var forgeCreds params.ForgeCredentials var ok bool switch payload.EntityType { case dbCommon.GithubCredentialsEntityType, dbCommon.GiteaCredentialsEntityType: - idGetter, ok = payload.Payload.(params.ForgeCredentials) + forgeCreds, ok = payload.Payload.(params.ForgeCredentials) default: return false } if !ok { return false } - return idGetter.GetID() == creds.GetID() + // Gite and Github creds have different models. The ID is uint, so we + // need to explicitly check their type, or risk a clash. + if forgeCreds.ForgeType != creds.ForgeType { + return false + } + return forgeCreds.GetID() == creds.GetID() } } diff --git a/database/watcher/util_test.go b/database/watcher/util_test.go new file mode 100644 index 00000000..82b94491 --- /dev/null +++ b/database/watcher/util_test.go @@ -0,0 +1,16 @@ +package watcher_test + +import ( + "time" + + "github.com/cloudbase/garm/database/common" +) + +func waitForPayload(ch <-chan common.ChangePayload, timeout time.Duration) *common.ChangePayload { + select { + case payload := <-ch: + return &payload + case <-time.After(timeout): + return nil + } +} diff --git a/database/watcher/watcher_test.go b/database/watcher/watcher_test.go index b272bda7..ab4653a9 100644 --- a/database/watcher/watcher_test.go +++ b/database/watcher/watcher_test.go @@ -20,13 +20,16 @@ import ( "testing" "time" + "github.com/google/uuid" "github.com/pkg/errors" "github.com/stretchr/testify/suite" + commonParams "github.com/cloudbase/garm-provider-common/params" "github.com/cloudbase/garm/database" "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/database/watcher" garmTesting "github.com/cloudbase/garm/internal/testing" + "github.com/cloudbase/garm/params" ) type WatcherTestSuite struct { @@ -50,6 +53,7 @@ func (s *WatcherTestSuite) TearDownTest() { currentWatcher := watcher.GetWatcher() if currentWatcher != nil { currentWatcher.Close() + watcher.SetWatcher(nil) } } @@ -126,7 +130,7 @@ func (s *WatcherTestSuite) TestProducerAndConsumer() { s.Require().Equal(payload, receivedPayload) } -func (s *WatcherTestSuite) TestConsumetWithFilter() { +func (s *WatcherTestSuite) TestConsumeWithFilter() { producer, err := watcher.RegisterProducer(s.ctx, "test-producer") s.Require().NoError(err) s.Require().NotNil(producer) @@ -146,12 +150,9 @@ func (s *WatcherTestSuite) TestConsumetWithFilter() { err = producer.Notify(payload) s.Require().NoError(err) - select { - case receivedPayload := <-consumer.Watch(): - s.Require().Equal(payload, receivedPayload) - case <-time.After(1 * time.Second): - s.T().Fatal("expected payload not received") - } + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) payload = common.ChangePayload{ EntityType: common.ControllerEntityType, @@ -161,11 +162,141 @@ func (s *WatcherTestSuite) TestConsumetWithFilter() { err = producer.Notify(payload) s.Require().NoError(err) - select { - case <-consumer.Watch(): - s.T().Fatal("unexpected payload received") - case <-time.After(1 * time.Second): + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithAnyFilter() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithAny( + watcher.WithEntityTypeFilter(common.ControllerEntityType), + watcher.WithEntityFilter(params.ForgeEntity{ + EntityType: params.ForgeEntityTypeRepository, + Owner: "test", + Name: "test", + ID: "test", + }), + )) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.ControllerEntityType, + Operation: common.UpdateOperation, + Payload: "test", } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.RepositoryEntityType, + Operation: common.UpdateOperation, + Payload: params.Repository{ + Owner: "test", + Name: "test", + ID: "test", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + // We're not watching for this repo + payload = common.ChangePayload{ + EntityType: common.RepositoryEntityType, + Operation: common.UpdateOperation, + Payload: params.Repository{ + Owner: "test", + Name: "test", + ID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + // We're not watching for orgs + payload = common.ChangePayload{ + EntityType: common.OrganizationEntityType, + Operation: common.UpdateOperation, + Payload: params.Repository{ + Owner: "test", + Name: "test", + ID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithAllFilter() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithAll( + watcher.WithEntityFilter(params.ForgeEntity{ + EntityType: params.ForgeEntityTypeRepository, + Owner: "test", + Name: "test", + ID: "test", + }), + watcher.WithOperationTypeFilter(common.CreateOperation), + )) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.RepositoryEntityType, + Operation: common.CreateOperation, + Payload: params.Repository{ + Owner: "test", + Name: "test", + ID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.RepositoryEntityType, + Operation: common.UpdateOperation, + Payload: params.Repository{ + Owner: "test", + Name: "test", + ID: "test", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) } func maybeInitController(db common.Store) error { @@ -180,6 +311,1127 @@ func maybeInitController(db common.Store) error { return nil } +func (s *WatcherTestSuite) TestWithEntityPoolFilterRepository() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeRepository, + Owner: "test", + Name: "test", + ID: "test", + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityPoolFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.PoolEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{ + ID: "test", + RepoID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.PoolEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{ + ID: "test", + RepoID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityPoolFilterOrg() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + ID: "test", + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityPoolFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.PoolEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{ + ID: "test", + OrgID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.PoolEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{ + ID: "test", + OrgID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityPoolFilterEnterprise() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeEnterprise, + Name: "test", + ID: "test", + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityPoolFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.PoolEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{ + ID: "test", + EnterpriseID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.PoolEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{ + ID: "test", + EnterpriseID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + // Invalid payload for declared entity type + payload = common.ChangePayload{ + EntityType: common.PoolEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + EnterpriseID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityPoolFilterBogusEntityType() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + // This should trigger the default branch in the filter and + // return false + EntityType: params.ForgeEntityType("bogus"), + Name: "test", + ID: "test", + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityPoolFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.PoolEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{ + ID: "test", + EnterpriseID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.PoolEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{ + ID: "test", + EnterpriseID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityScaleSetFilterRepository() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeRepository, + Owner: "test", + Name: "test", + ID: "test", + Credentials: params.ForgeCredentials{ + ForgeType: params.GithubEndpointType, + }, + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityScaleSetFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + RepoID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + RepoID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityScaleSetFilterOrg() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + ID: "test", + Credentials: params.ForgeCredentials{ + ForgeType: params.GithubEndpointType, + }, + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityScaleSetFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + OrgID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + OrgID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityScaleSetFilterEnterprise() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeEnterprise, + Name: "test", + ID: "test", + Credentials: params.ForgeCredentials{ + ForgeType: params.GithubEndpointType, + }, + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityScaleSetFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + EnterpriseID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + EnterpriseID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityScaleSetFilterBogusEntityType() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + // This should trigger the default branch in the filter and + // return false + EntityType: params.ForgeEntityType("bogus"), + Name: "test", + ID: "test", + Credentials: params.ForgeCredentials{ + ForgeType: params.GithubEndpointType, + }, + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityScaleSetFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + EnterpriseID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + EnterpriseID: "test2", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityScaleSetFilterReturnsFalseForGiteaEndpoints() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeRepository, + Owner: "test", + Name: "test", + ID: "test", + Credentials: params.ForgeCredentials{ + ForgeType: params.GiteaEndpointType, + }, + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityScaleSetFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + RepoID: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityFilterRepository() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeRepository, + Owner: "test", + Name: "test", + ID: "test", + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.RepositoryEntityType, + Operation: common.UpdateOperation, + Payload: params.Repository{ + ID: "test", + Name: "test", + Owner: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.RepositoryEntityType, + Operation: common.UpdateOperation, + Payload: params.Repository{ + ID: "test2", + Name: "test", + Owner: "test", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityFilterOrg() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + ID: "test", + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.OrganizationEntityType, + Operation: common.UpdateOperation, + Payload: params.Organization{ + ID: "test", + Name: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.OrganizationEntityType, + Operation: common.UpdateOperation, + Payload: params.Organization{ + ID: "test2", + Name: "test", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityFilterEnterprise() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeEnterprise, + Name: "test", + ID: "test", + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.EnterpriseEntityType, + Operation: common.UpdateOperation, + Payload: params.Enterprise{ + ID: "test", + Name: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.EnterpriseEntityType, + Operation: common.UpdateOperation, + Payload: params.Enterprise{ + ID: "test2", + Name: "test", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityJobFilterRepository() { + repoUUID, err := uuid.NewUUID() + s.Require().NoError(err) + + repoUUID2, err := uuid.NewUUID() + s.Require().NoError(err) + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeRepository, + Owner: "test", + Name: "test", + ID: repoUUID.String(), + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityJobFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.JobEntityType, + Operation: common.UpdateOperation, + Payload: params.Job{ + ID: 1, + Name: "test", + RepoID: &repoUUID, + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.JobEntityType, + Operation: common.UpdateOperation, + Payload: params.Job{ + ID: 1, + Name: "test", + RepoID: &repoUUID2, + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityJobFilterOrg() { + orgUUID, err := uuid.NewUUID() + s.Require().NoError(err) + + orgUUID2, err := uuid.NewUUID() + s.Require().NoError(err) + + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeOrganization, + Name: "test", + ID: orgUUID.String(), + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityJobFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.JobEntityType, + Operation: common.UpdateOperation, + Payload: params.Job{ + ID: 1, + Name: "test", + OrgID: &orgUUID, + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.JobEntityType, + Operation: common.UpdateOperation, + Payload: params.Job{ + ID: 1, + Name: "test", + OrgID: &orgUUID2, + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityJobFilterEnterprise() { + entUUID, err := uuid.NewUUID() + s.Require().NoError(err) + + entUUID2, err := uuid.NewUUID() + s.Require().NoError(err) + + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + EntityType: params.ForgeEntityTypeEnterprise, + Name: "test", + ID: entUUID.String(), + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityJobFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.JobEntityType, + Operation: common.UpdateOperation, + Payload: params.Job{ + ID: 1, + Name: "test", + EnterpriseID: &entUUID, + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.JobEntityType, + Operation: common.UpdateOperation, + Payload: params.Job{ + ID: 1, + Name: "test", + EnterpriseID: &entUUID2, + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithEntityJobFilterBogusEntityType() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + entity := params.ForgeEntity{ + // This should trigger the default branch in the filter and + // return false + EntityType: params.ForgeEntityType("bogus"), + Name: "test", + ID: "test", + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithEntityJobFilter(entity), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.JobEntityType, + Operation: common.UpdateOperation, + Payload: params.Job{ + ID: 1, + Name: "test", + EnterpriseID: nil, + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.JobEntityType, + Operation: common.UpdateOperation, + Payload: params.Job{ + ID: 1, + Name: "test", + EnterpriseID: nil, + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithNone() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithNone(), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.RepositoryEntityType, + Operation: common.UpdateOperation, + Payload: params.Repository{ + ID: "test", + Name: "test", + Owner: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithUserIDFilter() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + userID, err := uuid.NewUUID() + s.Require().NoError(err) + + userID2, err := uuid.NewUUID() + s.Require().NoError(err) + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithUserIDFilter(userID.String()), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.UserEntityType, + Operation: common.UpdateOperation, + Payload: params.User{ + ID: userID.String(), + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.UserEntityType, + Operation: common.UpdateOperation, + Payload: params.User{ + ID: userID2.String(), + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.UserEntityType, + Operation: common.UpdateOperation, + // Declare as user, but payload is a pool. Filter should return false. + Payload: params.Pool{}, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithForgeCredentialsGithub() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + creds := params.ForgeCredentials{ + ForgeType: params.GithubEndpointType, + ID: 1, + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithForgeCredentialsFilter(creds), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.GithubCredentialsEntityType, + Operation: common.UpdateOperation, + Payload: params.ForgeCredentials{ + ForgeType: params.GithubEndpointType, + ID: 1, + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.GiteaCredentialsEntityType, + Operation: common.UpdateOperation, + Payload: params.ForgeCredentials{ + ForgeType: params.GiteaEndpointType, + ID: 1, + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.GiteaCredentialsEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{}, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithcaleSetFilter() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + scaleSet := params.ScaleSet{ + ID: 1, + } + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithScaleSetFilter(scaleSet), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 1, + Name: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.ScaleSet{ + ID: 2, + Name: "test", + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.ScaleSetEntityType, + Operation: common.UpdateOperation, + Payload: params.Pool{}, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) +} + +func (s *WatcherTestSuite) TestWithExcludeEntityTypeFilter() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithExcludeEntityTypeFilter(common.RepositoryEntityType), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.RepositoryEntityType, + Operation: common.UpdateOperation, + Payload: params.Repository{ + ID: "test", + Name: "test", + Owner: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.OrganizationEntityType, + Operation: common.UpdateOperation, + Payload: params.Repository{ + ID: "test", + Name: "test", + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) +} + +func (s *WatcherTestSuite) TestWithInstanceStatusFilter() { + producer, err := watcher.RegisterProducer(s.ctx, "test-producer") + s.Require().NoError(err) + s.Require().NotNil(producer) + + consumer, err := watcher.RegisterConsumer( + s.ctx, "test-consumer", + watcher.WithInstanceStatusFilter( + commonParams.InstanceCreating, + commonParams.InstanceDeleting), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + + payload := common.ChangePayload{ + EntityType: common.InstanceEntityType, + Operation: common.UpdateOperation, + Payload: params.Instance{ + ID: "test-instance", + Status: commonParams.InstanceCreating, + }, + } + err = producer.Notify(payload) + s.Require().NoError(err) + + receivedPayload := waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.InstanceEntityType, + Operation: common.UpdateOperation, + Payload: params.Instance{ + ID: "test-instance", + Status: commonParams.InstanceDeleted, + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().Nil(receivedPayload) + + payload = common.ChangePayload{ + EntityType: common.InstanceEntityType, + Operation: common.UpdateOperation, + Payload: params.Instance{ + ID: "test-instance", + Status: commonParams.InstanceDeleting, + }, + } + + err = producer.Notify(payload) + s.Require().NoError(err) + receivedPayload = waitForPayload(consumer.Watch(), 100*time.Millisecond) + s.Require().NotNil(receivedPayload) + s.Require().Equal(payload, *receivedPayload) +} + func TestWatcherTestSuite(t *testing.T) { // Watcher tests watcherSuite := &WatcherTestSuite{ From 1fc72ab5c8c1f6d4f2bf3dcdb4a014939070733e Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Tue, 20 May 2025 16:11:37 +0000 Subject: [PATCH 21/21] Add more tests Signed-off-by: Gabriel Adrian Samfira --- database/watcher/watcher_store_test.go | 94 +++++++++++++++++++++++++- database/watcher/watcher_test.go | 27 ++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/database/watcher/watcher_store_test.go b/database/watcher/watcher_store_test.go index 8fe7d0a4..e682270a 100644 --- a/database/watcher/watcher_store_test.go +++ b/database/watcher/watcher_store_test.go @@ -919,6 +919,98 @@ func (s *WatcherStoreTestSuite) TestGithubCredentialsWatcher() { } } +func (s *WatcherStoreTestSuite) TestGiteaCredentialsWatcher() { + consumer, err := watcher.RegisterConsumer( + s.ctx, "gitea-cred-test", + watcher.WithEntityTypeFilter(common.GiteaCredentialsEntityType), + watcher.WithAny( + watcher.WithOperationTypeFilter(common.CreateOperation), + watcher.WithOperationTypeFilter(common.UpdateOperation), + watcher.WithOperationTypeFilter(common.DeleteOperation)), + ) + s.Require().NoError(err) + s.Require().NotNil(consumer) + s.T().Cleanup(func() { consumer.Close() }) + consumeEvents(consumer) + + testEndpointParams := params.CreateGiteaEndpointParams{ + Name: "test", + Description: "test endpoint", + APIBaseURL: "https://api.gitea.example.com", + BaseURL: "https://gitea.example.com", + } + + testEndpoint, err := s.store.CreateGiteaEndpoint(s.ctx, testEndpointParams) + s.Require().NoError(err) + s.Require().NotEmpty(testEndpoint.Name) + + s.T().Cleanup(func() { + if err := s.store.DeleteGiteaEndpoint(s.ctx, testEndpoint.Name); err != nil { + s.T().Logf("failed to delete Gitea endpoint: %v", err) + } + consumeEvents(consumer) + }) + + giteaCredParams := params.CreateGiteaCredentialsParams{ + Name: "test-creds", + Description: "test credentials", + Endpoint: testEndpoint.Name, + AuthType: params.ForgeAuthTypePAT, + PAT: params.GithubPAT{ + OAuth2Token: "bogus", + }, + } + + giteaCred, err := s.store.CreateGiteaCredentials(s.ctx, giteaCredParams) + s.Require().NoError(err) + s.Require().NotEmpty(giteaCred.ID) + + select { + case event := <-consumer.Watch(): + s.Require().Equal(common.ChangePayload{ + EntityType: common.GiteaCredentialsEntityType, + Operation: common.CreateOperation, + Payload: giteaCred, + }, event) + case <-time.After(1 * time.Second): + s.T().Fatal("expected payload not received") + } + + newDesc := "updated test description" + updateParams := params.UpdateGiteaCredentialsParams{ + Description: &newDesc, + } + + updatedGiteaCred, err := s.store.UpdateGiteaCredentials(s.ctx, giteaCred.ID, updateParams) + s.Require().NoError(err) + s.Require().Equal(newDesc, updatedGiteaCred.Description) + + select { + case event := <-consumer.Watch(): + s.Require().Equal(common.ChangePayload{ + EntityType: common.GiteaCredentialsEntityType, + Operation: common.UpdateOperation, + Payload: updatedGiteaCred, + }, event) + case <-time.After(1 * time.Second): + s.T().Fatal("expected payload not received") + } + + err = s.store.DeleteGiteaCredentials(s.ctx, giteaCred.ID) + s.Require().NoError(err) + + select { + case event := <-consumer.Watch(): + asCreds, ok := event.Payload.(params.ForgeCredentials) + s.Require().True(ok) + s.Require().Equal(event.Operation, common.DeleteOperation) + s.Require().Equal(event.EntityType, common.GiteaCredentialsEntityType) + s.Require().Equal(asCreds.ID, updatedGiteaCred.ID) + case <-time.After(1 * time.Second): + s.T().Fatal("expected payload not received") + } +} + func (s *WatcherStoreTestSuite) TestGithubEndpointWatcher() { consumer, err := watcher.RegisterConsumer( s.ctx, "gh-ep-test", @@ -1001,7 +1093,7 @@ consume: if !ok { return } - case <-time.After(100 * time.Millisecond): + case <-time.After(20 * time.Millisecond): break consume } } diff --git a/database/watcher/watcher_test.go b/database/watcher/watcher_test.go index ab4653a9..5b7ecdce 100644 --- a/database/watcher/watcher_test.go +++ b/database/watcher/watcher_test.go @@ -61,6 +61,7 @@ func (s *WatcherTestSuite) TestRegisterConsumerTwiceWillError() { consumer, err := watcher.RegisterConsumer(s.ctx, "test") s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) consumer, err = watcher.RegisterConsumer(s.ctx, "test") s.Require().ErrorIs(err, common.ErrConsumerAlreadyRegistered) @@ -117,6 +118,7 @@ func (s *WatcherTestSuite) TestProducerAndConsumer() { watcher.WithOperationTypeFilter(common.UpdateOperation)) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.ControllerEntityType, @@ -141,6 +143,7 @@ func (s *WatcherTestSuite) TestConsumeWithFilter() { watcher.WithOperationTypeFilter(common.UpdateOperation)) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.ControllerEntityType, @@ -184,6 +187,7 @@ func (s *WatcherTestSuite) TestWithAnyFilter() { )) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.ControllerEntityType, @@ -265,6 +269,7 @@ func (s *WatcherTestSuite) TestWithAllFilter() { )) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.RepositoryEntityType, @@ -329,6 +334,7 @@ func (s *WatcherTestSuite) TestWithEntityPoolFilterRepository() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.PoolEntityType, @@ -377,6 +383,7 @@ func (s *WatcherTestSuite) TestWithEntityPoolFilterOrg() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.PoolEntityType, @@ -425,6 +432,7 @@ func (s *WatcherTestSuite) TestWithEntityPoolFilterEnterprise() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.PoolEntityType, @@ -490,6 +498,7 @@ func (s *WatcherTestSuite) TestWithEntityPoolFilterBogusEntityType() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.PoolEntityType, @@ -541,6 +550,7 @@ func (s *WatcherTestSuite) TestWithEntityScaleSetFilterRepository() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.ScaleSetEntityType, @@ -592,6 +602,7 @@ func (s *WatcherTestSuite) TestWithEntityScaleSetFilterOrg() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.ScaleSetEntityType, @@ -643,6 +654,7 @@ func (s *WatcherTestSuite) TestWithEntityScaleSetFilterEnterprise() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.ScaleSetEntityType, @@ -696,6 +708,7 @@ func (s *WatcherTestSuite) TestWithEntityScaleSetFilterBogusEntityType() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.ScaleSetEntityType, @@ -747,6 +760,7 @@ func (s *WatcherTestSuite) TestWithEntityScaleSetFilterReturnsFalseForGiteaEndpo ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.ScaleSetEntityType, @@ -781,6 +795,7 @@ func (s *WatcherTestSuite) TestWithEntityFilterRepository() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.RepositoryEntityType, @@ -831,6 +846,7 @@ func (s *WatcherTestSuite) TestWithEntityFilterOrg() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.OrganizationEntityType, @@ -879,6 +895,7 @@ func (s *WatcherTestSuite) TestWithEntityFilterEnterprise() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.EnterpriseEntityType, @@ -933,6 +950,7 @@ func (s *WatcherTestSuite) TestWithEntityJobFilterRepository() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.JobEntityType, @@ -989,6 +1007,7 @@ func (s *WatcherTestSuite) TestWithEntityJobFilterOrg() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.JobEntityType, @@ -1045,6 +1064,7 @@ func (s *WatcherTestSuite) TestWithEntityJobFilterEnterprise() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.JobEntityType, @@ -1097,6 +1117,7 @@ func (s *WatcherTestSuite) TestWithEntityJobFilterBogusEntityType() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.JobEntityType, @@ -1140,6 +1161,7 @@ func (s *WatcherTestSuite) TestWithNone() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.RepositoryEntityType, @@ -1174,6 +1196,7 @@ func (s *WatcherTestSuite) TestWithUserIDFilter() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.UserEntityType, @@ -1231,6 +1254,7 @@ func (s *WatcherTestSuite) TestWithForgeCredentialsGithub() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.GithubCredentialsEntityType, @@ -1288,6 +1312,7 @@ func (s *WatcherTestSuite) TestWithcaleSetFilter() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.ScaleSetEntityType, @@ -1341,6 +1366,7 @@ func (s *WatcherTestSuite) TestWithExcludeEntityTypeFilter() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.RepositoryEntityType, @@ -1386,6 +1412,7 @@ func (s *WatcherTestSuite) TestWithInstanceStatusFilter() { ) s.Require().NoError(err) s.Require().NotNil(consumer) + consumeEvents(consumer) payload := common.ChangePayload{ EntityType: common.InstanceEntityType,