Esempio n. 1
0
func (r Run) start(id int) {
	for {
		cexpr, err := cronexpr.Parse(r.Cron)
		if err != nil {
			panic(err)
		}
		// sleep until the next run is scheduled to happen
		nrun := cexpr.Next(time.Now())
		waitduration := nrun.Sub(time.Now())
		log.Printf("[info] run %d will start at %v (in %v)", id, nrun, waitduration)
		time.Sleep(waitduration)

		notifchan := make(chan Notification)
		done := make(chan bool)
		go processNotifications(notifchan, done)
		var wg sync.WaitGroup
		for _, target := range r.Targets {
			log.Printf("[info] run %d starting scan of target %q", id, target)
			id, err := r.scan(target)
			debugprint("got scan id %d", id)
			if err != nil {
				log.Printf("[error] failed to launch against %q: %v", target, err)
				continue
			}
			wg.Add(1)
			go r.evaluate(id, notifchan, &wg)
		}
		wg.Wait()
		close(notifchan)
		<-done
	}
}
Esempio n. 2
0
// Start a scheduler entity. This is normally run in it's own go-routine
// and will wait until the configured time to execute.
func (e *entity) start() {
	xr := func(s string, args ...interface{}) {
		mlog(s, args...)
		e.deadChan <- true
	}

	e.abortRun = make(chan bool, 1)
	for {
		cexpr, err := cronexpr.Parse(e.cfg.Configuration.Schedule)
		if err != nil {
			xr("%v: bad cron expression: %v", e.name, err)
			return
		}
		nrun := cexpr.Next(time.Now())
		waitduration := nrun.Sub(time.Now())
		mlog("%v: will run at %v (in %v)", e.name, nrun, waitduration)
		select {
		case <-e.abortRun:
			mlog("%v: asked to terminate, stopping", e.name)
			return
		case <-time.After(waitduration):
		}
		mlog("%v: running", e.name)
		err = e.launchAction()
		if err != nil {
			xr("%v: %v", e.name, err)
			return
		}
	}
	mlog("%v: job exiting", e.name)
	e.deadChan <- true
}
func getVersions(request models.CheckRequest) (models.CheckResponse, error) {

	expr, err := cronexpr.Parse(request.Source.Expression)
	if err != nil {
		return nil, err
	}

	versions := []models.Version{}

	shouldFire := false

	loc, err := time.LoadLocation(request.Source.Location)
	if err != nil {
		return nil, err
	}

	previouslyFiredAt := request.Version.Time
	if previouslyFiredAt.IsZero() {
		previouslyFiredAt = time.Now().In(loc).Add(-1 * time.Hour)
	}

	shouldFireAt := expr.Next(previouslyFiredAt)
	if previouslyFiredAt.Before(shouldFireAt) && time.Now().After(shouldFireAt) {
		shouldFire = true
	}

	if shouldFire {
		versions = append(versions, models.Version{
			Time: time.Now().In(loc),
		})
	}

	return versions, nil
}
Esempio n. 4
0
// Parse returns a new Ticker containing a channel that will send the time
// with a period specified by the spec argument. Stop the ticker to release
// associated resources.
func Parse(spec string) (*Ticker, error) {
	expr, err := cronexpr.Parse(spec)
	if err != nil {
		return nil, err
	}

	tickerC := make(chan time.Time, 1)
	ticker := &Ticker{
		C:    tickerC,
		done: make(chan struct{}, 1),
		expr: expr,
	}

	go func() {
		for {
			next := ticker.expr.Next(c.Now())
			select {
			case <-time.After(next.Sub(c.Now())):
				tickerC <- c.Now()
			case <-ticker.done:
				break
			}
		}
	}()

	return ticker, nil
}
Esempio n. 5
0
// NewConfig parses the relevant environment variables, applying defaults if unspecified.
func NewConfig() (Config, error) {
	c := Config{}
	c.ErrInfo = os.Getenv("ERR_INFO") == "true"

	ibGw := os.Getenv("IB_GW")
	if ibGw == "" {
		ibGw = "127.0.0.1:4002"
	}
	c.IbGws = strings.Split(ibGw, ",")

	ibClientId := os.Getenv("IB_CID")
	if ibClientId == "" {
		ibClientId = "5555"
	}

	var err error
	c.IbClientId, err = strconv.Atoi(ibClientId)
	if err != nil {
		return c, fmt.Errorf("IB_CID '%s' not an integer")
	}

	c.DbUrl = os.Getenv("DB_URL")
	if c.DbUrl == "" {
		c.DbUrl = "postgres://ibc_dev@localhost/ibc_dev?sslmode=disable"
	}

	if !strings.HasPrefix(c.DbUrl, "postgres://") {
		return c, fmt.Errorf("DB_URL '%s' did not being with postgres://", c.DbUrl)
	}

	portString := os.Getenv("PORT")
	if portString == "" {
		portString = "3000"
	}

	c.Port, err = strconv.Atoi(portString)
	if err != nil {
		return c, fmt.Errorf("PORT '%s' not an integer")
	}

	c.Host = os.Getenv("HOST")
	if c.Host == "" {
		c.Host = "localhost"
	}

	acctRefresh := os.Getenv("ACCT_REF")
	if acctRefresh == "" {
		acctRefresh = "@hourly"
	}
	c.AccountRefresh, err = cronexpr.Parse(acctRefresh)
	if err != nil {
		return c, err
	}

	return c, nil
}
Esempio n. 6
0
// New returns a new cron expression.
// We use github.com/gorhill/cronexpr for parsing cron express inside.
func New(expr string) (schedule.Expression, error) {
	cronexpr, err := cronexpr.Parse(expr)
	if err != nil {
		return nil, err
	}

	return &expression{
		Expression: cronexpr,
	}, nil
}
Esempio n. 7
0
func (e *entityConfig) validate() error {
	if e.Configuration.Schedule == "" {
		return fmt.Errorf("missing schedule")
	}
	_, err := cronexpr.Parse(e.Configuration.Schedule)
	if err != nil {
		return fmt.Errorf("bad cron expression: %v", err)
	}
	return nil
}
Esempio n. 8
0
func (c *Channel) IsTimeToNotifyValid() bool {
	if c.TimeToNotify == ChannelSendImmediately {
		return true
	}

	_, err := cronexpr.Parse(c.TimeToNotify)
	if err != nil {
		logger.WithField("time_to_notify", c.TimeToNotify).Warn("failed to parse time to notify")
	}
	return err == nil
}
Esempio n. 9
0
func newCronTicker(cronExpr string) (*cronTicker, error) {
	expr, err := cronexpr.Parse(cronExpr)
	if err != nil {
		return nil, err
	}
	return &cronTicker{
		expr:    expr,
		ticker:  make(chan time.Time),
		closing: make(chan struct{}),
	}, nil
}
Esempio n. 10
0
func TestExpressions(t *testing.T) {
	for _, test := range crontests {
		for _, times := range test.times {
			from, _ := time.Parse("2006-01-02 15:04:05", times.from)
			expr, err := cronexpr.Parse(test.expr)
			if err != nil {
				t.Errorf(`cronexpr.Parse("%s") returned "%s"`, test.expr, err.Error())
			}
			next := expr.Next(from)
			nextstr := next.Format(test.layout)
			if nextstr != times.next {
				t.Errorf(`("%s").Next("%s") = "%s", got "%s"`, test.expr, times.from, times.next, nextstr)
			}
		}
	}
}
Esempio n. 11
0
func (e *Emissary) ShouldRun(t time.Time) (bool, error) {
	t = t.Truncate(time.Minute)
	compare := t.Add(-1 * time.Nanosecond)

	for _, s := range e.Schedules {
		parsed, err := cronexpr.Parse(s)
		if err != nil {
			return false, err
		}
		next := parsed.Next(compare)
		if next == t {
			return true, nil
		}
	}
	return false, nil
}
Esempio n. 12
0
// Init initializes some internal data inside the Alert, and must be called
// after the Alert is unmarshaled from yaml (or otherwise created)
func (a *Alert) Init() error {
	var err error
	a.searchIndexTPL, err = templatizeHelper(a.SearchIndex, err)
	a.searchTypeTPL, err = templatizeHelper(a.SearchType, err)
	a.searchTPL, err = templatizeHelper(&a.Search, err)
	if err != nil {
		return err
	}

	cron, err := cronexpr.Parse(a.Interval)
	if err != nil {
		return fmt.Errorf("parsing interval: %s", err)
	}
	a.cron = cron

	return nil
}
Esempio n. 13
0
func main() {
	var err error
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "%s - Manage users in various SaaS based on a LDAP source\n"+
			"Usage: %s -c config.yaml\n",
			os.Args[0], os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	if *showVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	// load the local configuration file
	conf, err := loadConf(*config)
	if err != nil {
		log.Fatalf("failed to load configuration: %v", err)
	}

	// just run once
	if *once || conf.Cron == "disabled" {
		run(conf)
		return
	}

	// infinite loop, wake up at the cron period
	for {
		cexpr, err := cronexpr.Parse(conf.Cron)
		if err != nil {
			log.Fatalf("failed to parse cron string %q: %v", conf.Cron, err)
		}
		// sleep until the next run is scheduled to happen
		nrun := cexpr.Next(time.Now())
		waitduration := nrun.Sub(time.Now())
		log.Printf("[info] next run will start at %v (in %v)", nrun, waitduration)
		time.Sleep(waitduration)

		run(conf)
	}
}
Esempio n. 14
0
// goroutine run this function periodly to check if this job is needed to auto start
func (j Job) NeedAutoStart() bool {
	if len(j.Schedule) > 0 {
		expr, err := cronexpr.Parse(j.Schedule)
		if err != nil {
			log.Debug(err.Error())
			return false
		}
		last_run_ts := j.GetLastRunTime()
		if last_run_ts >= 0 {
			last_run_time := time.Unix(last_run_ts, 0)
			next_time := expr.Next(last_run_time)
			log.Debug("next_time", next_time)
			// > 20s
			if time.Now().Unix()-next_time.Unix() > 20 {
				return true
			}
		}
	}
	return false
}
Esempio n. 15
0
// NewTaskRouteHandler creates a new task with the POST'd data
func NewTaskRouteHandler(ren *render.Render, dispatcher *dispatcher.Dispatcher) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var nt database.Task

		decoder := json.NewDecoder(r.Body)
		err := decoder.Decode(&nt)
		if err != nil {
			ren.JSON(w, 400, map[string]error{"error": err})
			return
		}
		defer r.Body.Close()

		switch {
		case nt.Name == "":
			ren.JSON(w, 400, map[string]error{"error": ErrMissingNameField})
			return
		case nt.CMD == "":
			ren.JSON(w, 400, map[string]error{"error": ErrMissingCMDField})
			return
		case nt.Args == "":
			ren.JSON(w, 400, map[string]error{"error": ErrMissingArgsField})
			return
		case nt.Schedule == "":
			ren.JSON(w, 400, map[string]error{"error": ErrMissingScheduleField})
			return
		}

		// validate that the entered cron string is valid.  Error if not.
		_, err = cronexpr.Parse(nt.Schedule)
		if err != nil {
			ren.JSON(w, 400, map[string]error{"error": err})
			return
		}

		dispatcher.SenderChan <- &nt
		dispatcher.TaskProcChan <- nt
		ren.JSON(w, http.StatusOK, map[string]database.Task{"task": nt})
	}
}
Esempio n. 16
0
// must check crontab  valid  first
func (cj *CronJob) IsValid() bool {
	log.Info("check crontab if avalid :", cj.Id, cj.Name)
	if _, err := cronexpr.Parse(cj.Schedule); err != nil {
		log.Warning("cron job parse crontab format failed: ", err)
		return false
	}
	if cj.Runner == "root" {
		log.Warning("cron job must run under non-root user, current is: ", cj.Runner)
		return false
	}

	now := time.Now().Unix()

	if cj.StartAt == 0 && cj.EndAt == 0 {
		return !cj.Disabled
	}

	if cj.StartAt == 1 && cj.EndAt == 2 {
		// delete job by user
		return false
	}

	if cj.StartAt > now && (cj.EndAt > cj.StartAt || cj.EndAt == 0) {
		log.Infof("crontab %d %s unstart, start: %d, end: %d", cj.Id, cj.Name, cj.StartAt, cj.EndAt)
		return false
	}

	if cj.StartAt < cj.EndAt && cj.EndAt < now && !cj.Disabled {
		log.Infof("crontab %d %s exprie, start: %d, end: %d", cj.Id, cj.Name, cj.StartAt, cj.EndAt)
		return false
	}

	if cj.StartAt < now && (cj.EndAt > now || cj.EndAt == 0) {
		log.Infof("crontab %d %s start, start: %d, end: %d", cj.Id, cj.Name, cj.StartAt, cj.EndAt)
		return !cj.Disabled
	}

	return !cj.Disabled
}
Esempio n. 17
0
// check crontab need
func (cj *CronJob) NeedSchedule() bool {

	expression, err := cronexpr.Parse(cj.Schedule)
	if err != nil {
		log.Warning("crontab parse failed: ", cj.Id, cj.Name)
		cj.Disabled = true
	}
	cj.expression = expression

	if cj.LastExecAt == 0 {
		cj.LastExecAt = cj.CreateAt
	}

	last_run_time := time.Unix(cj.LastExecAt, 0)
	nt := cj.expression.Next(last_run_time)
	// log.Info("cron next run is: ", cj.Id, cj.Name, nt)
	if time.Now().Unix()-nt.Unix() > 20 {
		// log.Info("needschedule true: ", cj.Id, cj.Name, nt)
		return true
	}
	// log.Info("needschedule false: ", cj.Id, cj.Name, nt)
	return false
}
Esempio n. 18
0
// Add creates a new cron job.
//
// Use the ID to Rem() that job later if you want.  F is the work to
// be performed.  The first argument is the scheduled time for that
// invocation, and the second argument is true if the job is a
// one-shot job.
//
// The schedule syntax can have three forms:
//
// 1. A cron schedule string (supposedly in syntax at
// https://en.wikipedia.org/wiki/Cron).
//
// 2. "!TIME", where TIME is according to RFC3339.
//
// 3. "+DURATION", where DURATION is a Go Duration
// (http://golang.org/pkg/time/#ParseDuration).  Examples: "5s" means
// "5 seconds", "2m" means "2 minutes", and "1h" means "1 hour".
func (c *Cron) Add(ctx *core.Context, id string, schedule string, f func(t time.Time) error) error {
	core.Log(core.INFO|CRON, ctx, "Cron.Add", "id", id, "schedule", schedule, "name", c.Name)
	job := CronJob{}
	job.Id = id
	job.Schedule = schedule
	job.Fn = f
	if core.OneShotSchedule(schedule) {
		switch schedule[0:1] {
		case "!":
			t, err := time.Parse(time.RFC3339, schedule[1:])
			if err != nil {
				return err
			}
			job.Next = t
		case "+":
			d, err := time.ParseDuration(schedule[1:])
			if err != nil {
				return err
			}
			job.Next = time.Now().Add(d)
		default:
			return fmt.Errorf("bad one-shot schedule '%s'", schedule)
		}
	} else {
		expr, err := cronexpr.Parse(schedule)
		if err != nil {
			return err
		}
		job.Expression = expr
	}

	future := job.Next.Sub(time.Now())
	core.Log(core.DEBUG|CRON, ctx, "Cron.Add", "id", id, "in", future)

	return c.schedule(ctx, &job, true)
}
Esempio n. 19
0
func (p Parser) ParseLine(line string) JobList {
	var jobList JobList = []Job{}
	re := regexp.MustCompile("^([^ ]+ +[^ ]+ +[^ ]+ +[^ ]+ +[^ ]+) +(.+)$")

	if !re.MatchString(line) {
		return jobList
	}

	matched := re.FindStringSubmatch(line)
	time := matched[1]
	script := matched[2]

	expr, err := cronexpr.Parse(time)
	if err != nil {
		return jobList
	}

	for nextTime := expr.Next(p.FromTime); !nextTime.After(p.ToTime); nextTime = expr.Next(nextTime) {
		job := NewJob(nextTime, script)
		jobList = append(jobList, *job)
	}

	return jobList
}
Esempio n. 20
0
func main() {
	var err error

	flag.Usage = usage
	flag.StringVar(&inTimeStr, "t", "", `whole or partial RFC3339 time value (i.e. "2006-01-02T15:04:05Z07:00") against which the cron expression is evaluated, now if not present`)
	flag.UintVar(&outTimeCount, "n", 1, `number of resulting time values to output`)
	flag.StringVar(&outTimeLayout, "l", "Mon, 02 Jan 2006 15:04:05 MST", `Go-compliant time layout to use for outputting time value(s), see <http://golang.org/pkg/time/#pkg-constants>`)
	flag.Parse()

	cronStr := flag.Arg(0)
	if len(cronStr) == 0 {
		flag.Usage()
		return
	}

	inTime := time.Now()
	inTimeLayout := ""
	timeStrLen := len(inTimeStr)
	if timeStrLen == 2 {
		inTimeLayout = "06"
	} else if timeStrLen >= 4 {
		inTimeLayout += "2006"
		if timeStrLen >= 7 {
			inTimeLayout += "-01"
			if timeStrLen >= 10 {
				inTimeLayout += "-02"
				if timeStrLen >= 13 {
					inTimeLayout += "T15"
					if timeStrLen >= 16 {
						inTimeLayout += ":04"
						if timeStrLen >= 19 {
							inTimeLayout += ":05"
							if timeStrLen >= 20 {
								inTimeLayout += "Z07:00"
							}
						}
					}
				}
			}
		}
	}

	if len(inTimeLayout) > 0 {
		// default to local time zone
		if timeStrLen < 20 {
			inTime, err = time.ParseInLocation(inTimeLayout, inTimeStr, time.Local)
		} else {
			inTime, err = time.Parse(inTimeLayout, inTimeStr)
		}
		if err != nil {
			fmt.Fprintf(os.Stderr, "# error: unparseable time value: \"%s\"\n", inTimeStr)
			os.Exit(1)
		}
	}

	expr, err := cronexpr.Parse(cronStr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "# %s: %s\n", os.Args[0], err)
		os.Exit(1)
	}

	// Anything on the output which starts with '#' can be ignored if the caller
	// is interested only in the time values. There is only one time
	// value per line, and they are always in chronological ascending order.
	fmt.Printf("# \"%s\" + \"%s\" =\n", cronStr, inTime.Format(time.RFC3339))

	if outTimeCount < 1 {
		outTimeCount = 1
	}
	outTimes := expr.NextN(inTime, outTimeCount)
	for _, outTime := range outTimes {
		fmt.Println(outTime.Format(outTimeLayout))
	}
}
Esempio n. 21
0
func Parse(in string) (*Schedule, error) {
	expr, err := cronexpr.Parse(in)
	return (*Schedule)(expr), err
}
Esempio n. 22
0
func main() {
	var (
		err  error
		conf conf
		cli  mozldap.Client
	)
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "%s - Manage users in various SaaS based on a LDAP source\n"+
			"Usage: %s -c config.yaml\n",
			os.Args[0], os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	if *drynotif {
		*dryrun = true
	}
	// safeguard, remove in prod
	*dryrun = true
	*drynotif = true

	// load the local configuration file
	fd, err := ioutil.ReadFile(*config)
	if err != nil {
		log.Fatal(err)
	}
	err = yaml.Unmarshal(fd, &conf)
	if err != nil {
		log.Fatalf("error: %v", err)
	}

	// infinite loop, wake up at the cron period
	for {
		if !*once {
			cexpr, err := cronexpr.Parse(conf.Cron)
			if err != nil {
				log.Fatal("failed to parse cron string %q: %v", conf.Cron, err)
			}
			// sleep until the next run is scheduled to happen
			nrun := cexpr.Next(time.Now())
			waitduration := nrun.Sub(time.Now())
			log.Printf("[info] next run will start at %v (in %v)", nrun, waitduration)
			time.Sleep(waitduration)
		}

		// instanciate an ldap client
		if conf.Ldap.TLSCert != "" && conf.Ldap.TLSKey != "" {
			cli, err = mozldap.NewTLSClient(
				conf.Ldap.Uri,
				conf.Ldap.Username,
				conf.Ldap.Password,
				conf.Ldap.TLSCert,
				conf.Ldap.TLSKey,
				conf.Ldap.CACert,
				&tls.Config{InsecureSkipVerify: conf.Ldap.Insecure})
		} else {
			cli, err = mozldap.NewClient(
				conf.Ldap.Uri,
				conf.Ldap.Username,
				conf.Ldap.Password,
				conf.Ldap.CACert,
				&tls.Config{InsecureSkipVerify: conf.Ldap.Insecure},
				conf.Ldap.Starttls)
		}
		if err != nil {
			log.Fatal(err)
		}
		defer cli.Close()
		log.Printf("connected %s on %s:%d, tls:%v starttls:%v\n", cli.BaseDN, cli.Host, cli.Port, cli.UseTLS, cli.UseStartTLS)
		conf.Ldap.cli = cli

		// Channel where modules publish their notifications
		// which are aggregated and sent by the main program
		notifchan := make(chan modules.Notification)
		notifdone := make(chan bool)
		go processNotifications(conf, notifchan, notifdone)

		// store the value of dryrun and the ldap client
		// in the configuration of each module
		for i := range conf.Modules {
			conf.Modules[i].DryRun = *dryrun
			conf.Modules[i].LdapCli = cli
			conf.Modules[i].Notify.Channel = notifchan
		}

		// run each module in the order it appears in the configuration
		for _, modconf := range conf.Modules {
			if *runmod != "all" && *runmod != modconf.Name {
				continue
			}
			if _, ok := modules.Available[modconf.Name]; !ok {
				log.Printf("[warning] %s module not registered, skipping it", modconf.Name)
				continue
			}
			log.Println("[info] invoking module", modconf.Name)
			run := modules.Available[modconf.Name].NewRun(modconf)
			err = run.Run()
			if err != nil {
				log.Printf("[error] %s module failed with error: %v", modconf.Name, err)
			}
		}
		// Modules are done, close the notification channel to tell the goroutine
		// that it can aggregate and send them, and wait for notifdone to come back
		close(notifchan)
		<-notifdone

		if *once {
			break
		}
	}
}