harness-drone/trigger/cron/cron.go

178 lines
4 KiB
Go
Raw Normal View History

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
}