feat: job config (#402)

John Olheiser created

* feat: job config

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* docs: add new config to readme

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* style: appease linter

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* review(aymanbagabas): add jobs to default config file template

Signed-off-by: jolheiser <john.olheiser@gmail.com>

---------

Signed-off-by: jolheiser <john.olheiser@gmail.com>

Change summary

README.md               |  4 ++++
server/config/config.go |  8 ++++++++
server/config/file.go   |  4 ++++
server/jobs/jobs.go     | 15 ++++++++++-----
server/jobs/mirror.go   | 17 ++++++++++++++---
server/server.go        |  2 +-
6 files changed, 41 insertions(+), 9 deletions(-)

Detailed changes

README.md 🔗

@@ -210,6 +210,10 @@ lfs:
   # Enable Git SSH transfer.
   ssh_enabled: true
 
+# Cron job configuration
+jobs:
+  mirror_pull: "@every 10m"
+
 # The stats server configuration.
 stats:
   # The address on which the stats server will listen.

server/config/config.go 🔗

@@ -108,6 +108,11 @@ type LFSConfig struct {
 	SSHEnabled bool `env:"SSH_ENABLED" yaml:"ssh_enabled"`
 }
 
+// JobsConfig is the configuration for cron jobs.
+type JobsConfig struct {
+	MirrorPull string `env:"MIRROR_PULL" yaml:"mirror_pull"`
+}
+
 // Config is the configuration for Soft Serve.
 type Config struct {
 	// Name is the name of the server.
@@ -134,6 +139,9 @@ type Config struct {
 	// LFS is the configuration for Git LFS.
 	LFS LFSConfig `envPrefix:"LFS_" yaml:"lfs"`
 
+	// Jobs is the configuration for cron jobs
+	Jobs JobsConfig `envPrefix:"JOBS_" yaml:"jobs"`
+
 	// InitialAdminKeys is a list of public keys that will be added to the list of admins.
 	InitialAdminKeys []string `env:"INITIAL_ADMIN_KEYS" envSeparator:"\n" yaml:"initial_admin_keys"`
 

server/config/file.go 🔗

@@ -102,6 +102,10 @@ lfs:
   # Enable Git SSH transfer.
   ssh_enabled: {{ .LFS.SSHEnabled }}
 
+# Cron job configuration
+jobs:
+  mirror_pull: "{{ .Jobs.MirrorPull }}"
+
 # Additional admin keys.
 #initial_admin_keys:
 #  - "ssh-rsa AAAAB3NzaC1yc2..."

server/jobs/jobs.go 🔗

@@ -7,9 +7,14 @@ import (
 
 // Job is a job that can be registered with the scheduler.
 type Job struct {
-	ID   int
-	Spec string
-	Func func(context.Context) func()
+	ID     int
+	Runner Runner
+}
+
+// Runner is a job runner.
+type Runner interface {
+	Spec(context.Context) string
+	Func(context.Context) func()
 }
 
 var (
@@ -18,10 +23,10 @@ var (
 )
 
 // Register registers a job.
-func Register(name, spec string, fn func(context.Context) func()) {
+func Register(name string, runner Runner) {
 	mtx.Lock()
 	defer mtx.Unlock()
-	jobs[name] = &Job{Spec: spec, Func: fn}
+	jobs[name] = &Job{Runner: runner}
 }
 
 // List returns a map of registered jobs.

server/jobs/mirror.go 🔗

@@ -17,11 +17,22 @@ import (
 )
 
 func init() {
-	Register("mirror-pull", "@every 10m", mirrorPull)
+	Register("mirror-pull", mirrorPull{})
 }
 
-// mirrorPull runs the (pull) mirror job task.
-func mirrorPull(ctx context.Context) func() {
+type mirrorPull struct{}
+
+// Spec derives the spec used for pull mirrors and implements Runner.
+func (m mirrorPull) Spec(ctx context.Context) string {
+	cfg := config.FromContext(ctx)
+	if cfg.Jobs.MirrorPull != "" {
+		return cfg.Jobs.MirrorPull
+	}
+	return "@every 10m"
+}
+
+// Func runs the (pull) mirror job task and implements Runner.
+func (m mirrorPull) Func(ctx context.Context) func() {
 	cfg := config.FromContext(ctx)
 	logger := log.FromContext(ctx).WithPrefix("jobs.mirror")
 	b := backend.FromContext(ctx)

server/server.go 🔗

@@ -57,7 +57,7 @@ func NewServer(ctx context.Context) (*Server, error) {
 	// Add cron jobs.
 	sched := cron.NewScheduler(ctx)
 	for n, j := range jobs.List() {
-		id, err := sched.AddFunc(j.Spec, j.Func(ctx))
+		id, err := sched.AddFunc(j.Runner.Spec(ctx), j.Runner.Func(ctx))
 		if err != nil {
 			logger.Warn("error adding cron job", "job", n, "err", err)
 		}