mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-27 04:35:16 +01:00
Merge remote-tracking branch 'upstream/job-service' into sync_image
This commit is contained in:
commit
f7897b53f3
76
dao/job.go
76
dao/job.go
@ -1,76 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/astaxie/beego/orm"
|
|
||||||
"github.com/vmware/harbor/models"
|
|
||||||
// "github.com/vmware/harbor/utils/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func AddJob(entry models.JobEntry) (int64, error) {
|
|
||||||
|
|
||||||
sql := `insert into job (job_type, status, options, parms, cron_str, creation_time, update_time) values (?,"pending",?,?,?,NOW(),NOW())`
|
|
||||||
o := orm.NewOrm()
|
|
||||||
p, err := o.Raw(sql).Prepare()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
r, err := p.Exec(entry.Type, entry.OptionsStr, entry.ParmsStr, entry.CronStr)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
id, err := r.LastInsertId()
|
|
||||||
return id, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddJobLog(id int64, level string, message string) error {
|
|
||||||
sql := `insert into job_log (job_id, level, message, creation_time, update_time) values (?, ?, ?, NOW(), NOW())`
|
|
||||||
//log.Debugf("trying to add a log for job:%d", id)
|
|
||||||
o := orm.NewOrm()
|
|
||||||
p, err := o.Raw(sql).Prepare()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = p.Exec(id, level, message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateJobStatus(id int64, status string) error {
|
|
||||||
o := orm.NewOrm()
|
|
||||||
sql := "update job set status=?, update_time=NOW() where job_id=?"
|
|
||||||
_, err := o.Raw(sql, status, id).Exec()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListJobs() ([]models.JobEntry, error) {
|
|
||||||
o := orm.NewOrm()
|
|
||||||
sql := `select j.job_id, j.job_type, j.status, j.enabled, j.creation_time, j.update_time from job j`
|
|
||||||
var res []models.JobEntry
|
|
||||||
_, err := o.Raw(sql).QueryRows(&res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetJob(id int64) (*models.JobEntry, error) {
|
|
||||||
o := orm.NewOrm()
|
|
||||||
sql := `select j.job_id, j.job_type, j.status, j.enabled, j.creation_time, j.update_time from job j where j.job_id = ?`
|
|
||||||
var res []models.JobEntry
|
|
||||||
p := make([]interface{}, 1)
|
|
||||||
p = append(p, id)
|
|
||||||
n, err := o.Raw(sql, p).QueryRows(&res)
|
|
||||||
if n == 0 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &res[0], err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetJobLogs(jobID int64) ([]models.JobLog, error) {
|
|
||||||
o := orm.NewOrm()
|
|
||||||
var res []models.JobLog
|
|
||||||
p := make([]interface{}, 1)
|
|
||||||
p = append(p, jobID)
|
|
||||||
sql := `select l.log_id, l.job_id, l.level, l.message, l.creation_time, l.update_time from job_log l where l.job_id = ?`
|
|
||||||
_, err := o.Raw(sql, p).QueryRows(&res)
|
|
||||||
return res, err
|
|
||||||
}
|
|
@ -58,18 +58,22 @@ func init() {
|
|||||||
log.Debugf("config: uiSecret: ******")
|
log.Debugf("config: uiSecret: ******")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaxJobWorkers ...
|
||||||
func MaxJobWorkers() int {
|
func MaxJobWorkers() int {
|
||||||
return maxJobWorkers
|
return maxJobWorkers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LocalHarborURL returns the local registry url, job service will use this URL to pull manifest and repository.
|
||||||
func LocalHarborURL() string {
|
func LocalHarborURL() string {
|
||||||
return localURL
|
return localURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LogDir returns the absolute path to which the log file will be written
|
||||||
func LogDir() string {
|
func LogDir() string {
|
||||||
return logDir
|
return logDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UISecret will return the value of secret cookie for jobsevice to call UI API.
|
||||||
func UISecret() string {
|
func UISecret() string {
|
||||||
return uiSecret
|
return uiSecret
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
package replication
|
|
||||||
|
|
||||||
type ImgOutParm struct {
|
|
||||||
Secret string `json:"secret"`
|
|
||||||
Image string `json:"image"`
|
|
||||||
Targets []*RegistryInfo `json:"targets"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RegistryInfo struct {
|
|
||||||
URL string `json:"url"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package job
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/vmware/harbor/models"
|
|
||||||
"github.com/vmware/harbor/utils/log"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JobRunner interface {
|
|
||||||
Run(je models.JobEntry) error
|
|
||||||
}
|
|
||||||
|
|
||||||
var runners map[string]*JobRunner = make(map[string]*JobRunner)
|
|
||||||
var runnerLock = &sync.Mutex{}
|
|
||||||
|
|
||||||
func Register(jobType string, runner JobRunner) {
|
|
||||||
runnerLock.Lock()
|
|
||||||
defer runnerLock.Unlock()
|
|
||||||
runners[jobType] = &runner
|
|
||||||
log.Debugf("runnter for job type:%s has been registered", jobType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunnerExists(jobType string) bool {
|
|
||||||
_, ok := runners[jobType]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(je models.JobEntry) error {
|
|
||||||
runner, ok := runners[je.Type]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Runner for job type: %s does not exist")
|
|
||||||
}
|
|
||||||
(*runner).Run(je)
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,7 +1,8 @@
|
|||||||
package job
|
package job
|
||||||
|
|
||||||
var JobQueue chan int64 = make(chan int64)
|
var jobQueue = make(chan int64)
|
||||||
|
|
||||||
|
// Schedule put a job id into job queue.
|
||||||
func Schedule(jobID int64) {
|
func Schedule(jobID int64) {
|
||||||
JobQueue <- jobID
|
jobQueue <- jobID
|
||||||
}
|
}
|
||||||
|
@ -18,41 +18,50 @@ type StateHandler interface {
|
|||||||
Exit() error
|
Exit() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DummyHandler is the default implementation of StateHander interface, which has empty Enter and Exit methods.
|
||||||
type DummyHandler struct {
|
type DummyHandler struct {
|
||||||
JobID int64
|
JobID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enter ...
|
||||||
func (dh DummyHandler) Enter() (string, error) {
|
func (dh DummyHandler) Enter() (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exit ...
|
||||||
func (dh DummyHandler) Exit() error {
|
func (dh DummyHandler) Exit() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatusUpdater implements the StateHandler interface which updates the status of a job in DB when the job enters
|
||||||
|
// a status.
|
||||||
type StatusUpdater struct {
|
type StatusUpdater struct {
|
||||||
DummyHandler
|
DummyHandler
|
||||||
State string
|
State string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enter updates the status of a job and returns "_continue" status to tell state machine to move on.
|
||||||
|
// If the status is a final status it returns empty string and the state machine will be stopped.
|
||||||
func (su StatusUpdater) Enter() (string, error) {
|
func (su StatusUpdater) Enter() (string, error) {
|
||||||
err := dao.UpdateRepJobStatus(su.JobID, su.State)
|
err := dao.UpdateRepJobStatus(su.JobID, su.State)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Failed to update state of job: %d, state: %s, error: %v", su.JobID, su.State, err)
|
log.Warningf("Failed to update state of job: %d, state: %s, error: %v", su.JobID, su.State, err)
|
||||||
}
|
}
|
||||||
var next string = models.JobContinue
|
var next = models.JobContinue
|
||||||
if su.State == models.JobStopped || su.State == models.JobError || su.State == models.JobFinished {
|
if su.State == models.JobStopped || su.State == models.JobError || su.State == models.JobFinished {
|
||||||
next = ""
|
next = ""
|
||||||
}
|
}
|
||||||
return next, err
|
return next, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImgPuller was for testing
|
||||||
type ImgPuller struct {
|
type ImgPuller struct {
|
||||||
DummyHandler
|
DummyHandler
|
||||||
img string
|
img string
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enter ...
|
||||||
func (ip ImgPuller) Enter() (string, error) {
|
func (ip ImgPuller) Enter() (string, error) {
|
||||||
ip.logger.Infof("I'm pretending to pull img:%s, then sleep 30s", ip.img)
|
ip.logger.Infof("I'm pretending to pull img:%s, then sleep 30s", ip.img)
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
@ -60,12 +69,14 @@ func (ip ImgPuller) Enter() (string, error) {
|
|||||||
return "push-img", nil
|
return "push-img", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImgPusher is a statehandler for testing
|
||||||
type ImgPusher struct {
|
type ImgPusher struct {
|
||||||
DummyHandler
|
DummyHandler
|
||||||
targetURL string
|
targetURL string
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enter ...
|
||||||
func (ip ImgPusher) Enter() (string, error) {
|
func (ip ImgPusher) Enter() (string, error) {
|
||||||
ip.logger.Infof("I'm pretending to push img to:%s, then sleep 30s", ip.targetURL)
|
ip.logger.Infof("I'm pretending to push img to:%s, then sleep 30s", ip.targetURL)
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/vmware/harbor/utils/log"
|
"github.com/vmware/harbor/utils/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RepJobParm wraps the parm of a job
|
||||||
type RepJobParm struct {
|
type RepJobParm struct {
|
||||||
LocalRegURL string
|
LocalRegURL string
|
||||||
TargetURL string
|
TargetURL string
|
||||||
@ -24,7 +25,8 @@ type RepJobParm struct {
|
|||||||
Operation string
|
Operation string
|
||||||
}
|
}
|
||||||
|
|
||||||
type JobSM struct {
|
// SM is the state machine to handle job, it handles one job at a time.
|
||||||
|
type SM struct {
|
||||||
JobID int64
|
JobID int64
|
||||||
CurrentState string
|
CurrentState string
|
||||||
PreviousState string
|
PreviousState string
|
||||||
@ -38,9 +40,9 @@ type JobSM struct {
|
|||||||
lock *sync.Mutex
|
lock *sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsterState transit the statemachine from the current state to the state in parameter.
|
// EnterState transit the statemachine from the current state to the state in parameter.
|
||||||
// It returns the next state the statemachine should tranit to.
|
// It returns the next state the statemachine should tranit to.
|
||||||
func (sm *JobSM) EnterState(s string) (string, error) {
|
func (sm *SM) EnterState(s string) (string, error) {
|
||||||
log.Debugf("Job id: %d, transiting from State: %s, to State: %s", sm.JobID, sm.CurrentState, s)
|
log.Debugf("Job id: %d, transiting from State: %s, to State: %s", sm.JobID, sm.CurrentState, s)
|
||||||
targets, ok := sm.Transitions[sm.CurrentState]
|
targets, ok := sm.Transitions[sm.CurrentState]
|
||||||
_, exist := targets[s]
|
_, exist := targets[s]
|
||||||
@ -57,7 +59,7 @@ func (sm *JobSM) EnterState(s string) (string, error) {
|
|||||||
log.Debugf("Job id: %d, no handler found for state:%s, skip", sm.JobID, sm.CurrentState)
|
log.Debugf("Job id: %d, no handler found for state:%s, skip", sm.JobID, sm.CurrentState)
|
||||||
}
|
}
|
||||||
enterHandler, ok := sm.Handlers[s]
|
enterHandler, ok := sm.Handlers[s]
|
||||||
var next string = models.JobContinue
|
var next = models.JobContinue
|
||||||
var err error
|
var err error
|
||||||
if ok {
|
if ok {
|
||||||
if next, err = enterHandler.Enter(); err != nil {
|
if next, err = enterHandler.Enter(); err != nil {
|
||||||
@ -75,7 +77,7 @@ func (sm *JobSM) EnterState(s string) (string, error) {
|
|||||||
// Start kicks off the statemachine to transit from current state to s, and moves on
|
// Start kicks off the statemachine to transit from current state to s, and moves on
|
||||||
// It will search the transit map if the next state is "_continue", and
|
// It will search the transit map if the next state is "_continue", and
|
||||||
// will enter error state if there's more than one possible path when next state is "_continue"
|
// will enter error state if there's more than one possible path when next state is "_continue"
|
||||||
func (sm *JobSM) Start(s string) {
|
func (sm *SM) Start(s string) {
|
||||||
n, err := sm.EnterState(s)
|
n, err := sm.EnterState(s)
|
||||||
log.Debugf("Job id: %d, next state from handler: %s", sm.JobID, n)
|
log.Debugf("Job id: %d, next state from handler: %s", sm.JobID, n)
|
||||||
for len(n) > 0 && err == nil {
|
for len(n) > 0 && err == nil {
|
||||||
@ -106,7 +108,8 @@ func (sm *JobSM) Start(s string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *JobSM) AddTransition(from string, to string, h StateHandler) {
|
// AddTransition add a transition to the transition table of state machine, the handler is the handler of target state "to"
|
||||||
|
func (sm *SM) AddTransition(from string, to string, h StateHandler) {
|
||||||
_, ok := sm.Transitions[from]
|
_, ok := sm.Transitions[from]
|
||||||
if !ok {
|
if !ok {
|
||||||
sm.Transitions[from] = make(map[string]struct{})
|
sm.Transitions[from] = make(map[string]struct{})
|
||||||
@ -115,7 +118,8 @@ func (sm *JobSM) AddTransition(from string, to string, h StateHandler) {
|
|||||||
sm.Handlers[to] = h
|
sm.Handlers[to] = h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *JobSM) RemoveTransition(from string, to string) {
|
// RemoveTransition removes a transition from transition table of the state machine
|
||||||
|
func (sm *SM) RemoveTransition(from string, to string) {
|
||||||
_, ok := sm.Transitions[from]
|
_, ok := sm.Transitions[from]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
@ -123,7 +127,9 @@ func (sm *JobSM) RemoveTransition(from string, to string) {
|
|||||||
delete(sm.Transitions[from], to)
|
delete(sm.Transitions[from], to)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *JobSM) Stop(id int64) {
|
// Stop will set the desired state as "stopped" such that when next tranisition happen the state machine will stop handling the current job
|
||||||
|
// and the worker can release itself to the workerpool.
|
||||||
|
func (sm *SM) Stop(id int64) {
|
||||||
log.Debugf("Trying to stop the job: %d", id)
|
log.Debugf("Trying to stop the job: %d", id)
|
||||||
sm.lock.Lock()
|
sm.lock.Lock()
|
||||||
defer sm.lock.Unlock()
|
defer sm.lock.Unlock()
|
||||||
@ -136,19 +142,20 @@ func (sm *JobSM) Stop(id int64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *JobSM) getDesiredState() string {
|
func (sm *SM) getDesiredState() string {
|
||||||
sm.lock.Lock()
|
sm.lock.Lock()
|
||||||
defer sm.lock.Unlock()
|
defer sm.lock.Unlock()
|
||||||
return sm.desiredState
|
return sm.desiredState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *JobSM) setDesiredState(s string) {
|
func (sm *SM) setDesiredState(s string) {
|
||||||
sm.lock.Lock()
|
sm.lock.Lock()
|
||||||
defer sm.lock.Unlock()
|
defer sm.lock.Unlock()
|
||||||
sm.desiredState = s
|
sm.desiredState = s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *JobSM) Init() {
|
// Init initialzie the state machine, it will be called once in the lifecycle of state machine.
|
||||||
|
func (sm *SM) Init() {
|
||||||
sm.lock = &sync.Mutex{}
|
sm.lock = &sync.Mutex{}
|
||||||
sm.Handlers = make(map[string]StateHandler)
|
sm.Handlers = make(map[string]StateHandler)
|
||||||
sm.Transitions = make(map[string]map[string]struct{})
|
sm.Transitions = make(map[string]map[string]struct{})
|
||||||
@ -159,7 +166,8 @@ func (sm *JobSM) Init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *JobSM) Reset(jid int64) error {
|
// Reset resets the state machine so it will start handling another job.
|
||||||
|
func (sm *SM) Reset(jid int64) error {
|
||||||
//To ensure the new jobID is visible to the thread to stop the SM
|
//To ensure the new jobID is visible to the thread to stop the SM
|
||||||
sm.lock.Lock()
|
sm.lock.Lock()
|
||||||
sm.JobID = jid
|
sm.JobID = jid
|
||||||
@ -234,7 +242,7 @@ func (sm *JobSM) Reset(jid int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func addImgTransferTransition(sm *JobSM) error {
|
func addImgTransferTransition(sm *SM) error {
|
||||||
base, err := replication.InitBaseHandler(sm.Parms.Repository, sm.Parms.LocalRegURL, config.UISecret(),
|
base, err := replication.InitBaseHandler(sm.Parms.Repository, sm.Parms.LocalRegURL, config.UISecret(),
|
||||||
sm.Parms.TargetURL, sm.Parms.TargetUsername, sm.Parms.TargetPassword,
|
sm.Parms.TargetURL, sm.Parms.TargetUsername, sm.Parms.TargetPassword,
|
||||||
sm.Parms.Tags, sm.Logger)
|
sm.Parms.Tags, sm.Logger)
|
||||||
@ -250,7 +258,7 @@ func addImgTransferTransition(sm *JobSM) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addImgDeleteTransition(sm *JobSM) error {
|
func addImgDeleteTransition(sm *SM) error {
|
||||||
deleter := replication.NewDeleter(sm.Parms.Repository, sm.Parms.Tags, sm.Parms.TargetURL,
|
deleter := replication.NewDeleter(sm.Parms.Repository, sm.Parms.Tags, sm.Parms.TargetURL,
|
||||||
sm.Parms.TargetUsername, sm.Parms.TargetPassword, sm.Logger)
|
sm.Parms.TargetUsername, sm.Parms.TargetPassword, sm.Logger)
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewLogger create a logger for a speicified job
|
||||||
func NewLogger(jobID int64) *log.Logger {
|
func NewLogger(jobID int64) *log.Logger {
|
||||||
logFile := GetJobLogPath(jobID)
|
logFile := GetJobLogPath(jobID)
|
||||||
f, err := os.OpenFile(logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
|
f, err := os.OpenFile(logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
|
||||||
@ -19,6 +20,7 @@ func NewLogger(jobID int64) *log.Logger {
|
|||||||
return log.New(f, log.NewTextFormatter(), log.InfoLevel)
|
return log.New(f, log.NewTextFormatter(), log.InfoLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetJobLogPath returns the absolute path in which the job log file is located.
|
||||||
func GetJobLogPath(jobID int64) string {
|
func GetJobLogPath(jobID int64) string {
|
||||||
fn := fmt.Sprintf("job_%d.log", jobID)
|
fn := fmt.Sprintf("job_%d.log", jobID)
|
||||||
return filepath.Join(config.LogDir(), fn)
|
return filepath.Join(config.LogDir(), fn)
|
||||||
|
@ -12,8 +12,11 @@ type workerPool struct {
|
|||||||
workerList []*Worker
|
workerList []*Worker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WorkerPool is a set of workers each worker is associate to a statemachine for handling jobs.
|
||||||
|
// it consists of a channel for free workers and a list to all workers
|
||||||
var WorkerPool *workerPool
|
var WorkerPool *workerPool
|
||||||
|
|
||||||
|
// StopJobs accepts a list of jobs and will try to stop them if any of them is being executed by the worker.
|
||||||
func (wp *workerPool) StopJobs(jobs []int64) {
|
func (wp *workerPool) StopJobs(jobs []int64) {
|
||||||
log.Debugf("Works working on jobs: %v will be stopped", jobs)
|
log.Debugf("Works working on jobs: %v will be stopped", jobs)
|
||||||
for _, id := range jobs {
|
for _, id := range jobs {
|
||||||
@ -26,13 +29,16 @@ func (wp *workerPool) StopJobs(jobs []int64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Worker consists of a channel for job from which worker gets the next job to handle, and a pointer to a statemachine,
|
||||||
|
// the actual work to handle the job is done via state machine.
|
||||||
type Worker struct {
|
type Worker struct {
|
||||||
ID int
|
ID int
|
||||||
RepJobs chan int64
|
RepJobs chan int64
|
||||||
SM *JobSM
|
SM *SM
|
||||||
quit chan bool
|
quit chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start is a loop worker gets id from its channel and handle it.
|
||||||
func (w *Worker) Start() {
|
func (w *Worker) Start() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@ -51,6 +57,7 @@ func (w *Worker) Start() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop ...
|
||||||
func (w *Worker) Stop() {
|
func (w *Worker) Stop() {
|
||||||
go func() {
|
go func() {
|
||||||
w.quit <- true
|
w.quit <- true
|
||||||
@ -75,17 +82,19 @@ func (w *Worker) handleRepJob(id int64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewWorker returns a pointer to new instance of worker
|
||||||
func NewWorker(id int) *Worker {
|
func NewWorker(id int) *Worker {
|
||||||
w := &Worker{
|
w := &Worker{
|
||||||
ID: id,
|
ID: id,
|
||||||
RepJobs: make(chan int64),
|
RepJobs: make(chan int64),
|
||||||
quit: make(chan bool),
|
quit: make(chan bool),
|
||||||
SM: &JobSM{},
|
SM: &SM{},
|
||||||
}
|
}
|
||||||
w.SM.Init()
|
w.SM.Init()
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitWorkerPool create workers according to configuration.
|
||||||
func InitWorkerPool() {
|
func InitWorkerPool() {
|
||||||
WorkerPool = &workerPool{
|
WorkerPool = &workerPool{
|
||||||
workerChan: make(chan *Worker, config.MaxJobWorkers()),
|
workerChan: make(chan *Worker, config.MaxJobWorkers()),
|
||||||
@ -99,10 +108,11 @@ func InitWorkerPool() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispatch will listen to the jobQueue of job service and try to pick a free worker from the worker pool and assign the job to it.
|
||||||
func Dispatch() {
|
func Dispatch() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case job := <-JobQueue:
|
case job := <-jobQueue:
|
||||||
go func(jobID int64) {
|
go func(jobID int64) {
|
||||||
log.Debugf("Trying to dispatch job: %d", jobID)
|
log.Debugf("Trying to dispatch job: %d", jobID)
|
||||||
worker := <-WorkerPool.workerChan
|
worker := <-WorkerPool.workerChan
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func initRouters() {
|
func initRouters() {
|
||||||
beego.Router("/api/replicationJobs", &api.ReplicationJob{})
|
beego.Router("/api/jobs/replication", &api.ReplicationJob{})
|
||||||
beego.Router("/api/replicationJobs/:id/log", &api.ReplicationJob{}, "get:GetLog")
|
beego.Router("/api/jobs/replication/:id/log", &api.ReplicationJob{}, "get:GetLog")
|
||||||
beego.Router("/api/replicationJobs/actions", &api.ReplicationJob{}, "post:HandleAction")
|
beego.Router("/api/jobs/replication/actions", &api.ReplicationJob{}, "post:HandleAction")
|
||||||
}
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JobEntry struct {
|
|
||||||
ID int64 `orm:"column(job_id)" json:"job_id"`
|
|
||||||
Type string `orm:"column(job_type)" json:"job_type"`
|
|
||||||
OptionsStr string `orm:"column(options)"`
|
|
||||||
ParmsStr string `orm:"column(parms)"`
|
|
||||||
Status string `orm:"column(status)" json:"status"`
|
|
||||||
Options map[string]interface{} `json:"options"`
|
|
||||||
Parms map[string]interface{} `json:"parms"`
|
|
||||||
Enabled int `orm:"column(enabled)" json:"enabled"`
|
|
||||||
CronStr string `orm:"column(cron_str)" json:"cron_str"`
|
|
||||||
TriggeredBy string `orm:"column(triggered_by)" json:"triggered_by"`
|
|
||||||
CreationTime time.Time `orm:"creation_time" json:"creation_time"`
|
|
||||||
UpdateTime time.Time `orm:"update_time" json:"update_time"`
|
|
||||||
Logs []JobLog `json:"logs"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JobLog struct {
|
|
||||||
ID int64 `orm:"column(log_id)" json:"log_id"`
|
|
||||||
JobID int64 `orm:"column(job_id)" json:"job_id"`
|
|
||||||
Level string `orm:"column(level)" json:"level"`
|
|
||||||
Message string `orm:"column(message)" json:"message"`
|
|
||||||
CreationTime time.Time `orm:"creation_time" json:"creation_time"`
|
|
||||||
UpdateTime time.Time `orm:"update_time" json:"update_time"`
|
|
||||||
}
|
|
@ -5,20 +5,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
//JobPending ...
|
||||||
JobPending string = "pending"
|
JobPending string = "pending"
|
||||||
|
//JobRunning ...
|
||||||
JobRunning string = "running"
|
JobRunning string = "running"
|
||||||
|
//JobError ...
|
||||||
JobError string = "error"
|
JobError string = "error"
|
||||||
|
//JobStopped ...
|
||||||
JobStopped string = "stopped"
|
JobStopped string = "stopped"
|
||||||
|
//JobFinished ...
|
||||||
JobFinished string = "finished"
|
JobFinished string = "finished"
|
||||||
|
//JobCanceled ...
|
||||||
JobCanceled string = "canceled"
|
JobCanceled string = "canceled"
|
||||||
// statemachine will move to next possible state based on trasition table
|
//JobContinue is the status returned by statehandler to tell statemachine to move to next possible state based on trasition table.
|
||||||
JobContinue string = "_continue"
|
JobContinue string = "_continue"
|
||||||
|
//RepOpTransfer represents the operation of a job to transfer repository to a remote registry/harbor instance.
|
||||||
RepOpTransfer string = "transfer"
|
RepOpTransfer string = "transfer"
|
||||||
|
//RepOpDelete represents the operation of a job to remove repository from a remote registry/harbor instance.
|
||||||
RepOpDelete string = "delete"
|
RepOpDelete string = "delete"
|
||||||
//cookie name to contain the UI secret
|
//UISecretCookie is the cookie name to contain the UI secret
|
||||||
UISecretCookie string = "uisecret"
|
UISecretCookie string = "uisecret"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RepPolicy is the model for a replication policy, which associate to a project and a target (destination)
|
||||||
type RepPolicy struct {
|
type RepPolicy struct {
|
||||||
ID int64 `orm:"column(id)" json:"id"`
|
ID int64 `orm:"column(id)" json:"id"`
|
||||||
ProjectID int64 `orm:"column(project_id)" json:"project_id"`
|
ProjectID int64 `orm:"column(project_id)" json:"project_id"`
|
||||||
@ -33,6 +42,8 @@ type RepPolicy struct {
|
|||||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepJob is the model for a replication job, which is the execution unit on job service, currently it is used to transfer/remove
|
||||||
|
// a repository to/from a remote registry instance.
|
||||||
type RepJob struct {
|
type RepJob struct {
|
||||||
ID int64 `orm:"column(id)" json:"id"`
|
ID int64 `orm:"column(id)" json:"id"`
|
||||||
Status string `orm:"column(status)" json:"status"`
|
Status string `orm:"column(status)" json:"status"`
|
||||||
@ -46,6 +57,7 @@ type RepJob struct {
|
|||||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepTarget is the model for a replication targe, i.e. destination, which wraps the endpoint URL and username/password of a remote registry.
|
||||||
type RepTarget struct {
|
type RepTarget struct {
|
||||||
ID int64 `orm:"column(id)" json:"id"`
|
ID int64 `orm:"column(id)" json:"id"`
|
||||||
URL string `orm:"column(url)" json:"url"`
|
URL string `orm:"column(url)" json:"url"`
|
||||||
@ -56,14 +68,17 @@ type RepTarget struct {
|
|||||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TableName is required by by beego orm to map RepTarget to table replication_target
|
||||||
func (rt *RepTarget) TableName() string {
|
func (rt *RepTarget) TableName() string {
|
||||||
return "replication_target"
|
return "replication_target"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TableName is required by by beego orm to map RepJob to table replication_job
|
||||||
func (rj *RepJob) TableName() string {
|
func (rj *RepJob) TableName() string {
|
||||||
return "replication_job"
|
return "replication_job"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TableName is required by by beego orm to map RepPolicy to table replication_policy
|
||||||
func (rp *RepPolicy) TableName() string {
|
func (rp *RepPolicy) TableName() string {
|
||||||
return "replication_policy"
|
return "replication_policy"
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Package utils contains methods to support security, cache, and webhook functions.
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -20,6 +22,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// VerifySecret verifies the UI_SECRET cookie in a http request.
|
||||||
func VerifySecret(r *http.Request) bool {
|
func VerifySecret(r *http.Request) bool {
|
||||||
secret := os.Getenv("UI_SECRET")
|
secret := os.Getenv("UI_SECRET")
|
||||||
c, err := r.Cookie("uisecret")
|
c, err := r.Cookie("uisecret")
|
||||||
|
@ -47,6 +47,8 @@ type cookieCredential struct {
|
|||||||
cookie *http.Cookie
|
cookie *http.Cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewCookieCredential initialize a cookie based crendential handler, the cookie in parameter will be added to request to registry
|
||||||
|
// if this crendential is attached to a registry client.
|
||||||
func NewCookieCredential(c *http.Cookie) Credential {
|
func NewCookieCredential(c *http.Cookie) Credential {
|
||||||
return &cookieCredential{
|
return &cookieCredential{
|
||||||
cookie: c,
|
cookie: c,
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// UserAgent is used to decorate the request so it can be identified by webhook.
|
||||||
UserAgent string = "registry-client"
|
UserAgent string = "registry-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -82,6 +83,8 @@ func NewRegistryWithUsername(endpoint, username string) (*Registry, error) {
|
|||||||
return registry, nil
|
return registry, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewRegistryWithCredential returns a Registry instance which associate to a crendential.
|
||||||
|
// And Credential is essentially a decorator for client to docorate the request before sending it to the registry.
|
||||||
func NewRegistryWithCredential(endpoint string, credential auth.Credential) (*Registry, error) {
|
func NewRegistryWithCredential(endpoint string, credential auth.Credential) (*Registry, error) {
|
||||||
endpoint = strings.TrimSpace(endpoint)
|
endpoint = strings.TrimSpace(endpoint)
|
||||||
endpoint = strings.TrimRight(endpoint, "/")
|
endpoint = strings.TrimRight(endpoint, "/")
|
||||||
|
Loading…
Reference in New Issue
Block a user