2019-02-19 23:56:41 +00:00
|
|
|
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
|
|
|
// Use of this source code is governed by the Drone Non-Commercial License
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
|
2019-02-28 07:07:13 +00:00
|
|
|
// +build !oss
|
|
|
|
|
2019-02-19 23:56:41 +00:00
|
|
|
package cron
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/drone/drone/core"
|
|
|
|
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
|
|
"github.com/robfig/cron"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
// New returns a new Cron scheduler.
|
|
|
|
func New(
|
|
|
|
commits core.CommitService,
|
|
|
|
cron core.CronStore,
|
|
|
|
repos core.RepositoryStore,
|
|
|
|
users core.UserStore,
|
|
|
|
trigger core.Triggerer,
|
|
|
|
) *Scheduler {
|
|
|
|
return &Scheduler{
|
|
|
|
commits: commits,
|
|
|
|
cron: cron,
|
|
|
|
repos: repos,
|
|
|
|
users: users,
|
|
|
|
trigger: trigger,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scheduler defines a cron scheduler.
|
|
|
|
type Scheduler struct {
|
|
|
|
commits core.CommitService
|
|
|
|
cron core.CronStore
|
|
|
|
repos core.RepositoryStore
|
|
|
|
users core.UserStore
|
|
|
|
trigger core.Triggerer
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start starts the cron scheduler.
|
|
|
|
func (s *Scheduler) Start(ctx context.Context, dur time.Duration) error {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
case <-time.After(dur):
|
|
|
|
s.run(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Scheduler) run(ctx context.Context) error {
|
|
|
|
var result error
|
|
|
|
|
|
|
|
logrus.Debugln("cron: begin process pending jobs")
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
logger := logrus.WithField("error", err)
|
|
|
|
logger.Errorln("cron: unexpected panic")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
jobs, err := s.cron.Ready(ctx, now.Unix())
|
|
|
|
if err != nil {
|
|
|
|
logger := logrus.WithError(err)
|
|
|
|
logger.Error("cron: cannot list pending jobs")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, job := range jobs {
|
|
|
|
// jobs can be manually disabled in the user interface,
|
|
|
|
// and should be skipped.
|
|
|
|
if job.Disabled {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
sched, err := cron.Parse(job.Expr)
|
|
|
|
if err != nil {
|
|
|
|
result = multierror.Append(result, err)
|
|
|
|
// this should never happen since we parse and verify
|
|
|
|
// the cron expression when the cron entry is created.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// calculate the next execution date.
|
|
|
|
job.Prev = job.Next
|
|
|
|
job.Next = sched.Next(now).Unix()
|
|
|
|
|
|
|
|
logger := logrus.WithFields(
|
|
|
|
logrus.Fields{
|
|
|
|
"repo": job.RepoID,
|
|
|
|
"cron": job.ID,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
err = s.cron.Update(ctx, job)
|
|
|
|
if err != nil {
|
|
|
|
logger := logrus.WithError(err)
|
|
|
|
logger.Warnln("cron: cannot re-schedule job")
|
|
|
|
result = multierror.Append(result, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
repo, err := s.repos.Find(ctx, job.RepoID)
|
|
|
|
if err != nil {
|
|
|
|
logger := logrus.WithError(err)
|
|
|
|
logger.Warnln("cron: cannot find repository")
|
|
|
|
result = multierror.Append(result, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
user, err := s.users.Find(ctx, repo.UserID)
|
|
|
|
if err != nil {
|
|
|
|
logger := logrus.WithError(err)
|
|
|
|
logger.Warnln("cron: cannot find repository owner")
|
|
|
|
result = multierror.Append(result, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(bradrydzewski) we may actually need to query the branch
|
|
|
|
// first to get the sha, and then query the commit. This works fine
|
|
|
|
// with github and gitlab, but may not work with other providers.
|
|
|
|
|
|
|
|
commit, err := s.commits.FindRef(ctx, user, repo.Slug, job.Branch)
|
|
|
|
if err != nil {
|
|
|
|
logger.WithFields(
|
|
|
|
logrus.Fields{
|
|
|
|
"error": err,
|
|
|
|
"repo": repo.Slug,
|
|
|
|
"branch": repo.Branch,
|
|
|
|
}).Warnln("cron: cannot find commit")
|
|
|
|
result = multierror.Append(result, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
hook := &core.Hook{
|
|
|
|
Trigger: core.TriggerCron,
|
|
|
|
Event: core.EventPush,
|
|
|
|
Link: commit.Link,
|
|
|
|
Timestamp: commit.Author.Date,
|
|
|
|
Message: commit.Message,
|
|
|
|
After: commit.Sha,
|
|
|
|
Ref: fmt.Sprintf("refs/heads/%s", job.Branch),
|
|
|
|
Target: job.Branch,
|
|
|
|
Author: commit.Author.Login,
|
|
|
|
AuthorName: commit.Author.Name,
|
|
|
|
AuthorEmail: commit.Author.Email,
|
|
|
|
AuthorAvatar: commit.Author.Avatar,
|
|
|
|
Sender: commit.Author.Login,
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = s.trigger.Trigger(ctx, repo, hook)
|
|
|
|
if err != nil {
|
|
|
|
logger.WithFields(
|
|
|
|
logrus.Fields{
|
|
|
|
"error": err,
|
|
|
|
"repo": repo.Slug,
|
|
|
|
"branch": repo.Branch,
|
|
|
|
"sha": commit.Sha,
|
|
|
|
}).Warnln("cron: cannot trigger build")
|
|
|
|
result = multierror.Append(result, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logrus.Debugf("cron: finished processing jobs")
|
|
|
|
return result
|
|
|
|
}
|