2018-03-08 06:16:57 +01:00
|
|
|
// Copyright 2018 The Harbor Authors. All rights reserved.
|
|
|
|
|
|
|
|
package runtime
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"sync"
|
|
|
|
"syscall"
|
2018-03-15 05:26:01 +01:00
|
|
|
"time"
|
2018-03-08 06:16:57 +01:00
|
|
|
|
2018-03-26 10:38:53 +02:00
|
|
|
"github.com/garyburd/redigo/redis"
|
2018-03-21 09:25:32 +01:00
|
|
|
"github.com/vmware/harbor/src/common/job"
|
2018-03-30 05:22:29 +02:00
|
|
|
"github.com/vmware/harbor/src/jobservice/api"
|
|
|
|
"github.com/vmware/harbor/src/jobservice/config"
|
|
|
|
"github.com/vmware/harbor/src/jobservice/core"
|
|
|
|
"github.com/vmware/harbor/src/jobservice/env"
|
|
|
|
"github.com/vmware/harbor/src/jobservice/job/impl"
|
|
|
|
"github.com/vmware/harbor/src/jobservice/job/impl/replication"
|
|
|
|
"github.com/vmware/harbor/src/jobservice/job/impl/scan"
|
|
|
|
"github.com/vmware/harbor/src/jobservice/logger"
|
|
|
|
"github.com/vmware/harbor/src/jobservice/pool"
|
2018-03-08 06:16:57 +01:00
|
|
|
)
|
|
|
|
|
2018-03-26 10:38:53 +02:00
|
|
|
const (
|
|
|
|
dialConnectionTimeout = 30 * time.Second
|
|
|
|
healthCheckPeriod = time.Minute
|
|
|
|
dialReadTimeout = healthCheckPeriod + 10*time.Second
|
|
|
|
dialWriteTimeout = 10 * time.Second
|
|
|
|
)
|
|
|
|
|
2018-03-08 06:16:57 +01:00
|
|
|
//JobService ...
|
|
|
|
var JobService = &Bootstrap{}
|
|
|
|
|
|
|
|
//Bootstrap is coordinating process to help load and start the other components to serve.
|
2018-03-23 10:26:41 +01:00
|
|
|
type Bootstrap struct {
|
|
|
|
jobConextInitializer env.JobContextInitializer
|
|
|
|
}
|
|
|
|
|
|
|
|
//SetJobContextInitializer set the job context initializer
|
|
|
|
func (bs *Bootstrap) SetJobContextInitializer(initializer env.JobContextInitializer) {
|
|
|
|
if initializer != nil {
|
|
|
|
bs.jobConextInitializer = initializer
|
|
|
|
}
|
|
|
|
}
|
2018-03-08 06:16:57 +01:00
|
|
|
|
|
|
|
//LoadAndRun will load configurations, initialize components and then start the related process to serve requests.
|
|
|
|
//Return error if meet any problems.
|
2018-03-26 09:30:16 +02:00
|
|
|
func (bs *Bootstrap) LoadAndRun() {
|
2018-03-08 06:16:57 +01:00
|
|
|
//Create the root context
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
2018-03-13 16:58:07 +01:00
|
|
|
|
|
|
|
rootContext := &env.Context{
|
2018-03-08 06:16:57 +01:00
|
|
|
SystemContext: ctx,
|
|
|
|
WG: &sync.WaitGroup{},
|
2018-03-13 16:58:07 +01:00
|
|
|
ErrorChan: make(chan error, 1), //with 1 buffer
|
2018-03-08 06:16:57 +01:00
|
|
|
}
|
|
|
|
|
2018-03-14 11:24:53 +01:00
|
|
|
//Build specified job context
|
2018-03-23 10:26:41 +01:00
|
|
|
if bs.jobConextInitializer != nil {
|
|
|
|
if jobCtx, err := bs.jobConextInitializer(rootContext); err == nil {
|
|
|
|
rootContext.JobContext = jobCtx
|
|
|
|
} else {
|
2018-03-26 09:30:16 +02:00
|
|
|
logger.Fatalf("Failed to initialize job context: %s\n", err)
|
2018-03-23 10:26:41 +01:00
|
|
|
}
|
2018-03-14 11:24:53 +01:00
|
|
|
}
|
|
|
|
|
2018-03-08 06:16:57 +01:00
|
|
|
//Start the pool
|
2018-03-13 16:58:07 +01:00
|
|
|
var backendPool pool.Interface
|
2018-03-22 11:29:39 +01:00
|
|
|
if config.DefaultConfig.PoolConfig.Backend == config.JobServicePoolBackendRedis {
|
|
|
|
backendPool = bs.loadAndRunRedisWorkerPool(rootContext, config.DefaultConfig)
|
2018-03-08 06:16:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//Initialize controller
|
2018-03-13 16:58:07 +01:00
|
|
|
ctl := core.NewController(backendPool)
|
2018-03-08 06:16:57 +01:00
|
|
|
|
|
|
|
//Start the API server
|
2018-03-22 11:29:39 +01:00
|
|
|
apiServer := bs.loadAndRunAPIServer(rootContext, config.DefaultConfig, ctl)
|
2018-03-26 09:30:16 +02:00
|
|
|
logger.Infof("Server is started at %s:%d with %s", "", config.DefaultConfig.Port, config.DefaultConfig.Protocol)
|
2018-03-08 06:16:57 +01:00
|
|
|
|
2018-03-22 16:39:24 +01:00
|
|
|
//Start outdated log files sweeper
|
|
|
|
logSweeper := logger.NewSweeper(ctx, config.GetLogBasePath(), config.GetLogArchivePeriod())
|
|
|
|
logSweeper.Start()
|
|
|
|
|
2018-03-08 06:16:57 +01:00
|
|
|
//Block here
|
|
|
|
sig := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sig, os.Interrupt, syscall.SIGTERM, os.Kill)
|
2018-03-13 16:58:07 +01:00
|
|
|
select {
|
|
|
|
case <-sig:
|
|
|
|
case err := <-rootContext.ErrorChan:
|
2018-03-26 09:30:16 +02:00
|
|
|
logger.Errorf("Server error:%s\n", err)
|
2018-03-13 16:58:07 +01:00
|
|
|
}
|
2018-03-08 06:16:57 +01:00
|
|
|
|
|
|
|
//Call cancel to send termination signal to other interested parts.
|
|
|
|
cancel()
|
|
|
|
|
|
|
|
//Gracefully shutdown
|
2018-03-13 16:58:07 +01:00
|
|
|
apiServer.Stop()
|
2018-03-08 06:16:57 +01:00
|
|
|
|
2018-03-15 05:26:01 +01:00
|
|
|
//In case stop is called before the server is ready
|
|
|
|
close := make(chan bool, 1)
|
|
|
|
go func() {
|
|
|
|
timer := time.NewTimer(10 * time.Second)
|
|
|
|
defer timer.Stop()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-timer.C:
|
|
|
|
//Try again
|
|
|
|
apiServer.Stop()
|
|
|
|
case <-close:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
}()
|
|
|
|
|
2018-03-08 06:16:57 +01:00
|
|
|
rootContext.WG.Wait()
|
2018-03-15 05:26:01 +01:00
|
|
|
close <- true
|
|
|
|
|
2018-03-26 09:30:16 +02:00
|
|
|
logger.Infof("Server gracefully exit")
|
2018-03-08 06:16:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//Load and run the API server.
|
2018-03-22 11:29:39 +01:00
|
|
|
func (bs *Bootstrap) loadAndRunAPIServer(ctx *env.Context, cfg *config.Configuration, ctl *core.Controller) *api.Server {
|
2018-03-08 06:16:57 +01:00
|
|
|
//Initialized API server
|
2018-04-08 12:12:13 +02:00
|
|
|
authProvider := &api.SecretAuthenticator{}
|
2018-03-13 16:58:07 +01:00
|
|
|
handler := api.NewDefaultHandler(ctl)
|
2018-04-08 12:12:13 +02:00
|
|
|
router := api.NewBaseRouter(handler, authProvider)
|
2018-03-08 06:16:57 +01:00
|
|
|
serverConfig := api.ServerConfig{
|
|
|
|
Protocol: cfg.Protocol,
|
|
|
|
Port: cfg.Port,
|
|
|
|
}
|
|
|
|
if cfg.HTTPSConfig != nil {
|
|
|
|
serverConfig.Cert = cfg.HTTPSConfig.Cert
|
|
|
|
serverConfig.Key = cfg.HTTPSConfig.Key
|
|
|
|
}
|
|
|
|
|
2018-03-13 16:58:07 +01:00
|
|
|
server := api.NewServer(ctx, router, serverConfig)
|
2018-03-08 06:16:57 +01:00
|
|
|
//Start processes
|
|
|
|
server.Start()
|
2018-03-13 16:58:07 +01:00
|
|
|
|
|
|
|
return server
|
2018-03-08 06:16:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//Load and run the worker pool
|
2018-03-22 11:29:39 +01:00
|
|
|
func (bs *Bootstrap) loadAndRunRedisWorkerPool(ctx *env.Context, cfg *config.Configuration) pool.Interface {
|
2018-03-26 10:38:53 +02:00
|
|
|
redisPool := &redis.Pool{
|
|
|
|
MaxActive: 6,
|
|
|
|
MaxIdle: 6,
|
|
|
|
Wait: true,
|
|
|
|
Dial: func() (redis.Conn, error) {
|
2018-04-12 10:17:57 +02:00
|
|
|
return redis.DialURL(
|
|
|
|
cfg.PoolConfig.RedisPoolCfg.RedisURL,
|
2018-03-26 10:38:53 +02:00
|
|
|
redis.DialConnectTimeout(dialConnectionTimeout),
|
|
|
|
redis.DialReadTimeout(dialReadTimeout),
|
|
|
|
redis.DialWriteTimeout(dialWriteTimeout),
|
|
|
|
)
|
|
|
|
},
|
2018-03-08 06:16:57 +01:00
|
|
|
}
|
|
|
|
|
2018-03-26 10:38:53 +02:00
|
|
|
redisWorkerPool := pool.NewGoCraftWorkPool(ctx,
|
|
|
|
cfg.PoolConfig.RedisPoolCfg.Namespace,
|
|
|
|
cfg.PoolConfig.WorkerCount,
|
|
|
|
redisPool)
|
2018-03-13 16:58:07 +01:00
|
|
|
//Register jobs here
|
2018-03-27 12:17:46 +02:00
|
|
|
if err := redisWorkerPool.RegisterJob(impl.KnownJobDemo, (*impl.DemoJob)(nil)); err != nil {
|
2018-03-13 16:58:07 +01:00
|
|
|
//exit
|
|
|
|
ctx.ErrorChan <- err
|
|
|
|
return redisWorkerPool //avoid nil pointer issue
|
|
|
|
}
|
2018-03-20 04:01:32 +01:00
|
|
|
if err := redisWorkerPool.RegisterJobs(
|
|
|
|
map[string]interface{}{
|
2018-03-23 16:53:15 +01:00
|
|
|
job.ImageScanJob: (*scan.ClairJob)(nil),
|
|
|
|
job.ImageTransfer: (*replication.Transfer)(nil),
|
|
|
|
job.ImageDelete: (*replication.Deleter)(nil),
|
|
|
|
job.ImageReplicate: (*replication.Replicator)(nil),
|
2018-03-20 04:01:32 +01:00
|
|
|
}); err != nil {
|
2018-03-21 09:25:32 +01:00
|
|
|
//exit
|
|
|
|
ctx.ErrorChan <- err
|
|
|
|
return redisWorkerPool //avoid nil pointer issue
|
|
|
|
}
|
2018-03-13 16:58:07 +01:00
|
|
|
|
|
|
|
redisWorkerPool.Start()
|
|
|
|
|
|
|
|
return redisWorkerPool
|
2018-03-08 06:16:57 +01:00
|
|
|
}
|