Skip to content

Commit

Permalink
Reporter
Browse files Browse the repository at this point in the history
  • Loading branch information
dzsak committed Dec 6, 2023
1 parent 2b51c92 commit 195904c
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/dashboard/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ type Config struct {
GitopsUpdaterFeatureFlagString string `envconfig:"FEATURE_GITOPS_UPDATER"`
StackUpdaterFeatureFlagString string `envconfig:"FEATURE_STACK_UPDATER"`
BuiltinEnvFeatureFlagString string `envconfig:"FEATURE_BUILT_IN_ENV"`
WeeklySummaryFeatureFlag bool `envconfig:"FEATURE_WEEKLY_SUMMARY"`

AlertEvaluationFrequencySeconds int `envconfig:"ALERT_EVALUATION_FREQUENCY_SECONDS"`

Expand Down
5 changes: 5 additions & 0 deletions cmd/dashboard/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ func main() {
go repoCache.Run()
log.Info("Repo cache initialized")

if config.WeeklySummaryFeatureFlag {
weeklyReporter := worker.NewWeeklyReporter(store, repoCache, notificationsManager)
go weeklyReporter.Run()
}

imageBuildWorker := worker.NewImageBuildWorker(store, successfullImageBuilds)
go imageBuildWorker.Run()

Expand Down
8 changes: 8 additions & 0 deletions pkg/dashboard/store/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ func (db *Store) Alerts() ([]*model.Alert, error) {
return data, err
}

func (db *Store) AlertsWithinAWeek() ([]*model.Alert, error) {
query := sql.Stmt(db.driver, sql.SelectAlerts)
data := []*model.Alert{}
oneWeekAgo := time.Now().Add(-7 * time.Hour * 24).Unix()
err := meddler.QueryAll(db, &data, query, oneWeekAgo)
return data, err
}

func (db *Store) AlertsByState(status string) ([]*model.Alert, error) {
stmt := sql.Stmt(db.driver, sql.SelectAlertsByState)
data := []*model.Alert{}
Expand Down
175 changes: 175 additions & 0 deletions pkg/dashboard/worker/weeklyReporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package worker

import (
"fmt"
"time"

"github.com/gimlet-io/gimlet-cli/pkg/dashboard/gitops"
"github.com/gimlet-io/gimlet-cli/pkg/dashboard/model"
"github.com/gimlet-io/gimlet-cli/pkg/dashboard/notifications"
"github.com/gimlet-io/gimlet-cli/pkg/dashboard/store"
"github.com/gimlet-io/gimlet-cli/pkg/dx"
"github.com/gimlet-io/gimlet-cli/pkg/git/nativeGit"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/sirupsen/logrus"
)

var perf = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "a",
Help: "a",
}, []string{"function"})

type weeklyReporter struct {
store *store.Store
repoCache *nativeGit.RepoCache
notificationsManager *notifications.ManagerImpl
}

func NewWeeklyReporter(
store *store.Store,
repoCache *nativeGit.RepoCache,
notificationsManager *notifications.ManagerImpl,
) weeklyReporter {
return weeklyReporter{
store: store,
repoCache: repoCache,
notificationsManager: notificationsManager,
}
}

func (w *weeklyReporter) Run() {
for {
year, week := time.Now().ISOWeek()
fmt.Printf("%d-%d", year, week)
// if !sent {
deploys, rollbacks, mostTriggeredBy := w.deployments()
seconds, change := w.alerts()
lagSeconds := w.lag()
msg := notifications.WeeklySummary(deploys, rollbacks, mostTriggeredBy, seconds, change, lagSeconds, []string{})
w.notificationsManager.Broadcast(msg)
//}
time.Sleep(24 * time.Hour)
}
}

func (w *weeklyReporter) deployments() (deploys int, rollbacks int, overallMostTriggeredBy string) {
envs, _ := w.store.GetEnvironments()
oneWeekAgo := time.Now().Add(-7 * time.Hour * 24)

maxCount := 0
for _, env := range envs {
repo, pathToCleanUp, err := w.repoCache.InstanceForWriteWithHistory(env.AppsRepo) // using a copy of the repo to avoid concurrent map writes error
defer w.repoCache.CleanupWrittenRepo(pathToCleanUp)
if err != nil {
logrus.Errorf("cannot get gitops repo for write: %s", err)
continue
}

releases, err := gitops.Releases(repo, "", env.Name, env.RepoPerEnv, &oneWeekAgo, nil, -1, "", perf)
if err != nil {
logrus.Errorf("cannot get releases: %s", err)
continue
}

mostTriggered := mostTriggeredBy(releases)
count := len(releases)
if count > maxCount {
maxCount = count
overallMostTriggeredBy = mostTriggered
}

for _, r := range releases {
if r.RolledBack {
rollbacks++
} else {
deploys++
}
}
}

return deploys, rollbacks, overallMostTriggeredBy
}

func (w *weeklyReporter) alerts() (alertSeconds int, change float64) {
alerts, _ := w.store.AlertsWithinAWeek()
alertSeconds = seconds(alerts)

// TODO
// oldAlerts, _ := w.store.AlertsEarlier()
// oldAlertSeconds := seconds(oldAlerts)
oldAlertSeconds := 0
change = percentageChange(oldAlertSeconds, alertSeconds)
return alertSeconds, change
}

func seconds(alerts []*model.Alert) (seconds int) {
for _, a := range alerts {
if a.FiredAt == 0 {
continue
}

resolved := time.Now().Unix()
if a.ResolvedAt != 0 {
resolved = a.ResolvedAt
}

seconds += (int(resolved) - int(a.FiredAt))
}
return seconds
}

func percentageChange(old, new int) (delta float64) {
diff := float64(new - old)
delta = (diff / float64(old)) * 100
return
}

func (w *weeklyReporter) lag() (lagSeconds int64) { // TODO nil pointer error
prod, _ := w.store.GetEnvironment("production")
latestProdRelease := latestRelease(w.repoCache, prod)
staging, _ := w.store.GetEnvironment("staging")
latestStagingRelease := latestRelease(w.repoCache, staging)

if latestProdRelease == nil || latestStagingRelease == nil {
return 0
}

return latestStagingRelease.Created - latestProdRelease.Created
}

func latestRelease(repoCache *nativeGit.RepoCache, env *model.Environment) *dx.Release {
repo, pathToCleanUp, err := repoCache.InstanceForWriteWithHistory(env.AppsRepo) // using a copy of the repo to avoid concurrent map writes error
defer repoCache.CleanupWrittenRepo(pathToCleanUp)
if err != nil {
logrus.Errorf("cannot get gitops repo for write: %s", err)
return nil
}

releases, err := gitops.Releases(repo, "", env.Name, env.RepoPerEnv, nil, nil, 1, "", perf)
if err != nil {
logrus.Errorf("cannot get releases: %s", err)
return nil
}
return releases[0]
}

func mostTriggeredBy(releases []*dx.Release) (mostTriggerer string) {
triggerCount := make(map[string]int)

// Count occurrences of TriggeredBy names
for _, release := range releases {
triggerCount[release.TriggeredBy]++
}

// Find the name with the highest count
maxCount := 0
for name, count := range triggerCount {
if count > maxCount {
maxCount = count
mostTriggerer = name
}
}

return mostTriggerer
}
40 changes: 40 additions & 0 deletions pkg/dashboard/worker/weeklyReporter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package worker

import (
"testing"

"github.com/gimlet-io/gimlet-cli/pkg/dx"
"github.com/stretchr/testify/assert"
)

func Test_Percentage(t *testing.T) {
res := percentageChange(1000, 830)
assert.Equal(t, float64(-17.0), res)

// res = percentageChange(0, 0)
// assert.Equal(t, math.NaN(), res)

// // anything divided by 0 will become infinity
// res = percentageChange(0, 50)
// assert.Equal(t, math.Inf(1), res)
}

func Test_MostTriggerer(t *testing.T) {
releases := []*dx.Release{
{
TriggeredBy: "policy",
},
{
TriggeredBy: "dzsak",
},
{
TriggeredBy: "dzsak",
},
{
TriggeredBy: "dzsak",
},
}

name := mostTriggeredBy(releases)
assert.Equal(t, "dzsak", name)
}

0 comments on commit 195904c

Please sign in to comment.