Skip to content

Commit

Permalink
[usage] Setup controller and reconciler
Browse files Browse the repository at this point in the history
  • Loading branch information
easyCZ authored and roboquat committed Jun 1, 2022
1 parent bfcb0a9 commit 109c45d
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 2 deletions.
15 changes: 13 additions & 2 deletions components/usage/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ package cmd
import (
"github.com/gitpod-io/gitpod/common-go/baseserver"
"github.com/gitpod-io/gitpod/common-go/log"
"github.com/gitpod-io/gitpod/usage/pkg/controller"
"github.com/gitpod-io/gitpod/usage/pkg/db"
"github.com/spf13/cobra"
"net"
"os"
"time"
)

func init() {
Expand All @@ -29,8 +31,6 @@ func run() *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
log.Init(ServiceName, Version, true, verbose)

log.Info("Hello world usage server")

_, err := db.Connect(db.ConnectionParams{
User: os.Getenv("DB_USERNAME"),
Password: os.Getenv("DB_PASSWORD"),
Expand All @@ -41,6 +41,17 @@ func run() *cobra.Command {
log.WithError(err).Fatal("Failed to establish database connection.")
}

ctrl, err := controller.New(1*time.Minute, controller.ReconcilerFunc(controller.HelloWorldReconciler))
if err != nil {
log.WithError(err).Fatal("Failed to initialize usage controller.")
}

err = ctrl.Start()
if err != nil {
log.WithError(err).Fatal("Failed to start usage controller.")
}
defer ctrl.Stop()

srv, err := baseserver.New("usage")
if err != nil {
log.WithError(err).Fatal("Failed to initialize server.")
Expand Down
1 change: 1 addition & 0 deletions components/usage/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ require (
github.com/gitpod-io/gitpod/common-go v0.0.0-00010101000000-000000000000
github.com/go-sql-driver/mysql v1.6.0
github.com/relvacode/iso8601 v1.1.0
github.com/robfig/cron v1.2.0
github.com/spf13/cobra v1.4.0
github.com/stretchr/testify v1.7.0
gorm.io/datatypes v1.0.6
Expand Down
2 changes: 2 additions & 0 deletions components/usage/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/relvacode/iso8601 v1.1.0 h1:2nV8sp0eOjpoKQ2vD3xSDygsjAx37NHG2UlZiCkDH4I=
github.com/relvacode/iso8601 v1.1.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
Expand Down
66 changes: 66 additions & 0 deletions components/usage/pkg/controller/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License-AGPL.txt in the project root for license information.

package controller

import (
"fmt"
"github.com/gitpod-io/gitpod/common-go/log"
"github.com/robfig/cron"
"sync"
"time"
)

func New(schedule time.Duration, reconciler Reconciler) (*Controller, error) {
return &Controller{
schedule: schedule,
reconciler: reconciler,
scheduler: cron.NewWithLocation(time.UTC),
}, nil
}

type Controller struct {
schedule time.Duration
reconciler Reconciler

scheduler *cron.Cron

runningJobs sync.WaitGroup
}

func (c *Controller) Start() error {
log.Info("Starting usage controller.")

err := c.scheduler.AddFunc(fmt.Sprintf("@every %s", c.schedule.String()), cron.FuncJob(func() {
log.Info("Starting usage reconciliation.")

c.runningJobs.Add(1)
defer c.runningJobs.Done()

err := c.reconciler.Reconcile()
if err != nil {
log.WithError(err).Errorf("Reconciliation run failed.")
} else {
log.Info("Completed usage reconciliation run without errors.")
}
}))
if err != nil {
return fmt.Errorf("failed to add function to scheduler: %w", err)
}

c.scheduler.Start()

return nil
}

// Stop terminates the Controller and awaits for all running jobs to complete.
func (c *Controller) Stop() {
log.Info("Stopping usage controller.")
// Stop any new jobs from running
c.scheduler.Stop()

log.Info("Awaiting existing reconciliation runs to complete..")
// Wait for existing jobs to finish
c.runningJobs.Wait()
}
37 changes: 37 additions & 0 deletions components/usage/pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License-AGPL.txt in the project root for license information.

package controller

import (
"github.com/stretchr/testify/require"
"testing"
"time"
)

func TestController(t *testing.T) {
schedule := time.Second
triggered := false

ctrl, err := New(schedule, ReconcilerFunc(func() error {
triggered = true
return nil
}))
require.NoError(t, err)

require.NoError(t, ctrl.Start())
time.Sleep(schedule + 20*time.Millisecond)
require.True(t, triggered, "must trigger reconciler function")
ctrl.Stop()
}

func TestController_GracefullyHandlesPanic(t *testing.T) {
ctrl, err := New(20*time.Millisecond, ReconcilerFunc(func() error {
panic("pls help")
}))
require.NoError(t, err)

require.NoError(t, ctrl.Start())
ctrl.Stop()
}
22 changes: 22 additions & 0 deletions components/usage/pkg/controller/reconciler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License-AGPL.txt in the project root for license information.

package controller

import "github.com/gitpod-io/gitpod/common-go/log"

type Reconciler interface {
Reconcile() error
}

type ReconcilerFunc func() error

func (f ReconcilerFunc) Reconcile() error {
return f()
}

func HelloWorldReconciler() error {
log.Info("Hello world reconciler!")
return nil
}

0 comments on commit 109c45d

Please sign in to comment.