mirror of
https://github.com/go-gitea/gitea.git
synced 2025-04-18 00:47:48 -04:00
161 lines
2.8 KiB
Go
161 lines
2.8 KiB
Go
// Copyright 2022 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package queue
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/models/bots"
|
|
"code.gitea.io/gitea/routers/api/bots/core"
|
|
runnerv1 "gitea.com/gitea/proto-go/runner/v1"
|
|
)
|
|
|
|
type worker struct {
|
|
kind string
|
|
typ string
|
|
os string
|
|
arch string
|
|
channel chan *runnerv1.Stage
|
|
}
|
|
|
|
type queue struct {
|
|
sync.Mutex
|
|
|
|
ready chan struct{}
|
|
paused bool
|
|
interval time.Duration
|
|
workers map[*worker]struct{}
|
|
ctx context.Context
|
|
}
|
|
|
|
func (q *queue) Schedule(ctx context.Context, stage *runnerv1.Stage) error {
|
|
select {
|
|
case q.ready <- struct{}{}:
|
|
default:
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (q *queue) Request(ctx context.Context, params core.Filter) (*runnerv1.Stage, error) {
|
|
w := &worker{
|
|
kind: params.Kind,
|
|
typ: params.Type,
|
|
os: params.OS,
|
|
arch: params.Arch,
|
|
channel: make(chan *runnerv1.Stage),
|
|
}
|
|
q.Lock()
|
|
q.workers[w] = struct{}{}
|
|
q.Unlock()
|
|
|
|
select {
|
|
case q.ready <- struct{}{}:
|
|
default:
|
|
}
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
q.Lock()
|
|
delete(q.workers, w)
|
|
q.Unlock()
|
|
return nil, ctx.Err()
|
|
case b := <-w.channel:
|
|
return b, nil
|
|
}
|
|
}
|
|
|
|
func (q *queue) start() error {
|
|
for {
|
|
select {
|
|
case <-q.ctx.Done():
|
|
return q.ctx.Err()
|
|
case <-q.ready:
|
|
_ = q.signal(q.ctx)
|
|
case <-time.After(q.interval):
|
|
_ = q.signal(q.ctx)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (q *queue) signal(ctx context.Context) error {
|
|
q.Lock()
|
|
count := len(q.workers)
|
|
pause := q.paused
|
|
q.Unlock()
|
|
if pause {
|
|
return nil
|
|
}
|
|
if count == 0 {
|
|
return nil
|
|
}
|
|
items, err := bots.FindStages(ctx, bots.FindStageOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
q.Lock()
|
|
defer q.Unlock()
|
|
for _, item := range items {
|
|
if item.Status == bots.StatusRunning {
|
|
continue
|
|
}
|
|
if item.Machine != "" {
|
|
continue
|
|
}
|
|
|
|
loop:
|
|
for w := range q.workers {
|
|
// the worker must match the resource kind and type
|
|
if !matchResource(w.kind, w.typ, item.Kind, item.Type) {
|
|
continue
|
|
}
|
|
|
|
if w.os != "" || w.arch != "" {
|
|
if w.os != item.OS {
|
|
continue
|
|
}
|
|
if w.arch != item.Arch {
|
|
continue
|
|
}
|
|
}
|
|
|
|
stage := &runnerv1.Stage{
|
|
Id: item.ID,
|
|
BuildId: item.BuildID,
|
|
Name: item.Name,
|
|
Kind: item.Name,
|
|
Type: item.Type,
|
|
Status: string(item.Status),
|
|
Started: int64(item.Started),
|
|
Stopped: int64(item.Stopped),
|
|
}
|
|
|
|
w.channel <- stage
|
|
delete(q.workers, w)
|
|
break loop
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// matchResource is a helper function that returns
|
|
func matchResource(kinda, typea, kindb, typeb string) bool {
|
|
if kinda == "" {
|
|
kinda = "pipeline"
|
|
}
|
|
if kindb == "" {
|
|
kindb = "pipeline"
|
|
}
|
|
if typea == "" {
|
|
typea = "docker"
|
|
}
|
|
if typeb == "" {
|
|
typeb = "docker"
|
|
}
|
|
return kinda == kindb && typea == typeb
|
|
}
|