mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-19 23:28:20 +01:00
312 lines
7.1 KiB
Go
312 lines
7.1 KiB
Go
package scheduler
|
|
|
|
import (
|
|
"github.com/vmware/harbor/src/common/scheduler/policy"
|
|
"github.com/vmware/harbor/src/common/utils/log"
|
|
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
defaultQueueSize = 10
|
|
|
|
statSchedulePolicy = "Schedule Policy"
|
|
statUnSchedulePolicy = "Unschedule Policy"
|
|
statTaskRun = "Task Run"
|
|
statTaskComplete = "Task Complete"
|
|
statTaskFail = "Task Fail"
|
|
)
|
|
|
|
//StatItem is defined for the stat metrics.
|
|
type StatItem struct {
|
|
//Metrics catalog
|
|
Type string
|
|
|
|
//The stat value
|
|
Value uint32
|
|
|
|
//Attach some other info
|
|
Attachment interface{}
|
|
}
|
|
|
|
//StatSummary is used to collect some metrics of scheduler.
|
|
type StatSummary struct {
|
|
//Count of scheduled policy
|
|
PolicyCount uint32
|
|
|
|
//Total count of tasks
|
|
Tasks uint32
|
|
|
|
//Count of successfully complete tasks
|
|
CompletedTasks uint32
|
|
|
|
//Count of tasks with errors
|
|
TasksWithError uint32
|
|
}
|
|
|
|
//Configuration defines configuration of Scheduler.
|
|
type Configuration struct {
|
|
QueueSize uint8
|
|
}
|
|
|
|
//Scheduler is designed for scheduling policies.
|
|
type Scheduler struct {
|
|
//Mutex for sync controling.
|
|
*sync.RWMutex
|
|
|
|
//Related configuration options for scheduler.
|
|
config *Configuration
|
|
|
|
//Store to keep the references of scheduled policies.
|
|
policies Store
|
|
|
|
//Queue for receiving policy scheduling request
|
|
scheduleQueue chan *Watcher
|
|
|
|
//Queue for receiving policy unscheduling request or complete signal.
|
|
unscheduleQueue chan *Watcher
|
|
|
|
//Channel for receiving stat metrics.
|
|
statChan chan *StatItem
|
|
|
|
//Channel for terminate scheduler damon.
|
|
terminateChan chan bool
|
|
|
|
//The stat metrics of scheduler.
|
|
stats *StatSummary
|
|
|
|
//To indicate whether scheduler is running or not
|
|
isRunning bool
|
|
}
|
|
|
|
//DefaultScheduler is a default scheduler.
|
|
var DefaultScheduler = NewScheduler(nil)
|
|
|
|
//NewScheduler is constructor for creating a scheduler.
|
|
func NewScheduler(config *Configuration) *Scheduler {
|
|
var qSize uint8 = defaultQueueSize
|
|
if config != nil && config.QueueSize > 0 {
|
|
qSize = config.QueueSize
|
|
}
|
|
|
|
sq := make(chan *Watcher, qSize)
|
|
usq := make(chan *Watcher, qSize)
|
|
stChan := make(chan *StatItem, 4)
|
|
tc := make(chan bool, 1)
|
|
|
|
store := NewDefaultStore()
|
|
return &Scheduler{
|
|
RWMutex: new(sync.RWMutex),
|
|
config: config,
|
|
policies: store,
|
|
scheduleQueue: sq,
|
|
unscheduleQueue: usq,
|
|
statChan: stChan,
|
|
terminateChan: tc,
|
|
stats: &StatSummary{
|
|
PolicyCount: 0,
|
|
Tasks: 0,
|
|
CompletedTasks: 0,
|
|
TasksWithError: 0,
|
|
},
|
|
isRunning: false,
|
|
}
|
|
}
|
|
|
|
//Start the scheduler damon.
|
|
func (sch *Scheduler) Start() {
|
|
sch.Lock()
|
|
defer sch.Unlock()
|
|
|
|
//If scheduler is already running
|
|
if sch.isRunning {
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
log.Errorf("Runtime error in scheduler:%s\n", r)
|
|
}
|
|
}()
|
|
defer func() {
|
|
//Clear resources
|
|
sch.policies.Clear()
|
|
log.Infof("Policy scheduler stop at %s\n", time.Now().UTC().Format(time.RFC3339))
|
|
}()
|
|
for {
|
|
select {
|
|
case <-sch.terminateChan:
|
|
//Exit
|
|
return
|
|
case wt := <-sch.scheduleQueue:
|
|
//If status is stopped, no requests should be served
|
|
if !sch.IsRunning() {
|
|
continue
|
|
}
|
|
go func(watcher *Watcher) {
|
|
if watcher != nil && watcher.p != nil {
|
|
//Enable it.
|
|
watcher.Start()
|
|
|
|
//Update stats and log info.
|
|
log.Infof("Policy %s is scheduled", watcher.p.Name())
|
|
sch.statChan <- &StatItem{statSchedulePolicy, 1, nil}
|
|
}
|
|
}(wt)
|
|
case wt := <-sch.unscheduleQueue:
|
|
//If status is stopped, no requests should be served
|
|
if !sch.IsRunning() {
|
|
continue
|
|
}
|
|
go func(watcher *Watcher) {
|
|
if watcher != nil && watcher.IsRunning() {
|
|
watcher.Stop()
|
|
|
|
//Update stats and log info.
|
|
log.Infof("Policy %s is unscheduled", watcher.p.Name())
|
|
sch.statChan <- &StatItem{statUnSchedulePolicy, 1, nil}
|
|
}
|
|
}(wt)
|
|
case stat := <-sch.statChan:
|
|
{
|
|
//If status is stopped, no requests should be served
|
|
if !sch.IsRunning() {
|
|
continue
|
|
}
|
|
switch stat.Type {
|
|
case statSchedulePolicy:
|
|
sch.stats.PolicyCount += stat.Value
|
|
break
|
|
case statUnSchedulePolicy:
|
|
sch.stats.PolicyCount -= stat.Value
|
|
break
|
|
case statTaskRun:
|
|
sch.stats.Tasks += stat.Value
|
|
break
|
|
case statTaskComplete:
|
|
sch.stats.CompletedTasks += stat.Value
|
|
break
|
|
case statTaskFail:
|
|
sch.stats.TasksWithError += stat.Value
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
log.Infof("Policies:%d, Tasks:%d, CompletedTasks:%d, FailedTasks:%d\n",
|
|
sch.stats.PolicyCount,
|
|
sch.stats.Tasks,
|
|
sch.stats.CompletedTasks,
|
|
sch.stats.TasksWithError)
|
|
|
|
if stat.Attachment != nil &&
|
|
reflect.TypeOf(stat.Attachment).String() == "*errors.errorString" {
|
|
log.Errorf("%s: %s\n", stat.Type, stat.Attachment.(error).Error())
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}()
|
|
|
|
sch.isRunning = true
|
|
log.Infof("Policy scheduler start at %s\n", time.Now().UTC().Format(time.RFC3339))
|
|
}
|
|
|
|
//Stop the scheduler damon.
|
|
func (sch *Scheduler) Stop() {
|
|
//Lock for state changing
|
|
sch.Lock()
|
|
|
|
//Check if the scheduler is running
|
|
if !sch.isRunning {
|
|
sch.Unlock()
|
|
return
|
|
}
|
|
|
|
sch.isRunning = false
|
|
sch.Unlock()
|
|
|
|
//Terminate damon to stop receiving signals.
|
|
sch.terminateChan <- true
|
|
}
|
|
|
|
//Schedule and enable the policy.
|
|
func (sch *Scheduler) Schedule(scheduledPolicy policy.Policy) error {
|
|
if scheduledPolicy == nil {
|
|
return errors.New("nil is not Policy object")
|
|
}
|
|
|
|
if strings.TrimSpace(scheduledPolicy.Name()) == "" {
|
|
return errors.New("Policy should be assigned a name")
|
|
}
|
|
|
|
tasks := scheduledPolicy.Tasks()
|
|
if tasks == nil || len(tasks) == 0 {
|
|
return errors.New("Policy must attach task(s)")
|
|
}
|
|
|
|
//Try to schedule the policy.
|
|
//Keep the policy for future use after it's successfully scheduled.
|
|
watcher := NewWatcher(scheduledPolicy, sch.statChan, sch.unscheduleQueue)
|
|
if err := sch.policies.Put(scheduledPolicy.Name(), watcher); err != nil {
|
|
return err
|
|
}
|
|
|
|
//Schedule the policy
|
|
sch.scheduleQueue <- watcher
|
|
|
|
return nil
|
|
}
|
|
|
|
//UnSchedule the specified policy from the enabled policies list.
|
|
func (sch *Scheduler) UnSchedule(policyName string) error {
|
|
if strings.TrimSpace(policyName) == "" {
|
|
return errors.New("Empty policy name is invalid")
|
|
}
|
|
|
|
//Find the watcher.
|
|
watcher := sch.policies.Remove(policyName)
|
|
if watcher == nil {
|
|
return fmt.Errorf("Policy %s is not existing", policyName)
|
|
}
|
|
|
|
//Unschedule the policy.
|
|
sch.unscheduleQueue <- watcher
|
|
|
|
return nil
|
|
}
|
|
|
|
//IsRunning to indicate whether the scheduler is running.
|
|
func (sch *Scheduler) IsRunning() bool {
|
|
sch.RLock()
|
|
defer sch.RUnlock()
|
|
|
|
return sch.isRunning
|
|
}
|
|
|
|
//HasScheduled is to check whether the given policy has been scheduled or not.
|
|
func (sch *Scheduler) HasScheduled(policyName string) bool {
|
|
return sch.policies.Exists(policyName)
|
|
}
|
|
|
|
//GetPolicy is used to get related policy reference by its name.
|
|
func (sch *Scheduler) GetPolicy(policyName string) policy.Policy {
|
|
wk := sch.policies.Get(policyName)
|
|
if wk != nil {
|
|
return wk.p
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
//PolicyCount returns the count of currently scheduled policies in the scheduler.
|
|
func (sch *Scheduler) PolicyCount() uint32 {
|
|
return sch.policies.Size()
|
|
}
|