Merge remote-tracking branch 'upstream/job-service' into sync_image

This commit is contained in:
Wenkai Yin 2016-05-31 15:08:59 +08:00
commit f7897b53f3
15 changed files with 90 additions and 186 deletions

View File

@ -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
}

View File

@ -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
} }

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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
} }

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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")
} }

View File

@ -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"`
}

View File

@ -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"
} }

View File

@ -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")

View File

@ -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,

View File

@ -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, "/")