diff --git a/README.md b/README.md index 35b2537..467a264 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Mostly just a project to test [temporal.io](https://temporal.io/). - Spread workers across regions to monitor latency from different locations. - [x] Use [k6](https://github.com/grafana/k6) for checks, so that they can be written in javascript. - [ ] History and working home page. + - Kinda working atm. But look if all the data could be stored/fetched from temporal. - [ ] Edit/Delete operations for healthchecks and workers. - [ ] CronJob Healthchecks (via webhooks). - [ ] Notifications (webhooks, slack, etc). diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 14cba9e..dbb1758 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -6,7 +6,6 @@ import ( "code.tjo.space/mentos1386/zdravko/web/templates/components" "github.com/gorilla/sessions" "go.temporal.io/sdk/client" - "gorm.io/gorm" ) var Pages = []*components.Page{ @@ -25,7 +24,6 @@ func GetPageByTitle(pages []*components.Page, title string) *components.Page { } type BaseHandler struct { - db *gorm.DB query *query.Query config *config.ServerConfig @@ -34,8 +32,8 @@ type BaseHandler struct { store *sessions.CookieStore } -func NewBaseHandler(db *gorm.DB, q *query.Query, temporal client.Client, config *config.ServerConfig) *BaseHandler { +func NewBaseHandler(q *query.Query, temporal client.Client, config *config.ServerConfig) *BaseHandler { store := sessions.NewCookieStore([]byte(config.SessionSecret)) - return &BaseHandler{db, q, config, temporal, store} + return &BaseHandler{q, config, temporal, store} } diff --git a/internal/handlers/oauth2.go b/internal/handlers/oauth2.go index 2c9da8d..ce2f3f6 100644 --- a/internal/handlers/oauth2.go +++ b/internal/handlers/oauth2.go @@ -82,12 +82,13 @@ func (h *BaseHandler) RefreshToken(w http.ResponseWriter, r *http.Request, user } func (h *BaseHandler) OAuth2LoginGET(c echo.Context) error { + ctx := context.Background() conf := newOAuth2(h.config) state := newRandomState() - result := h.db.Create(&models.OAuth2State{State: state, Expiry: time.Now().Add(5 * time.Minute)}) - if result.Error != nil { - return result.Error + err := h.query.OAuth2State.WithContext(ctx).Create(&models.OAuth2State{State: state, Expiry: time.Now().Add(5 * time.Minute)}) + if err != nil { + return err } url := conf.AuthCodeURL(state, oauth2.AccessTypeOffline) diff --git a/internal/handlers/settingshealthchecks.go b/internal/handlers/settingshealthchecks.go index affbaf9..9f5a85b 100644 --- a/internal/handlers/settingshealthchecks.go +++ b/internal/handlers/settingshealthchecks.go @@ -70,6 +70,46 @@ func (h *BaseHandler) SettingsHealthchecksDescribeGET(c echo.Context) error { }) } +func (h *BaseHandler) SettingsHealthchecksDescribePOST(c echo.Context) error { + ctx := context.Background() + + slug := c.Param("slug") + + healthcheck, err := services.GetHealthcheck(ctx, h.query, slug) + if err != nil { + return err + } + + update := &models.Healthcheck{ + Slug: healthcheck.Slug, + Name: healthcheck.Name, + Schedule: c.FormValue("schedule"), + WorkerGroups: strings.Split(c.FormValue("workergroups"), " "), + Script: c.FormValue("script"), + } + + err = validator.New(validator.WithRequiredStructEnabled()).Struct(update) + if err != nil { + return err + } + + err = services.UpdateHealthcheck( + ctx, + h.query, + update, + ) + if err != nil { + return err + } + + err = services.CreateOrUpdateHealthcheckSchedule(ctx, h.temporal, healthcheck) + if err != nil { + return err + } + + return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/healthchecks/%s", slug)) +} + func (h *BaseHandler) SettingsHealthchecksCreateGET(c echo.Context) error { cc := c.(AuthenticatedContext) @@ -101,14 +141,14 @@ func (h *BaseHandler) SettingsHealthchecksCreatePOST(c echo.Context) error { err = services.CreateHealthcheck( ctx, - h.db, + h.query, healthcheckHttp, ) if err != nil { return err } - err = services.StartHealthcheck(ctx, h.temporal, healthcheckHttp) + err = services.CreateOrUpdateHealthcheckSchedule(ctx, h.temporal, healthcheckHttp) if err != nil { return err } diff --git a/internal/handlers/settingsworkers.go b/internal/handlers/settingsworkers.go index 769ed66..6f697e7 100644 --- a/internal/handlers/settingsworkers.go +++ b/internal/handlers/settingsworkers.go @@ -113,7 +113,7 @@ func (h *BaseHandler) SettingsWorkersCreatePOST(c echo.Context) error { err = services.CreateWorker( ctx, - h.db, + h.query, worker, ) if err != nil { diff --git a/internal/services/healthcheck.go b/internal/services/healthcheck.go index 1eb2f51..5d7eb33 100644 --- a/internal/services/healthcheck.go +++ b/internal/services/healthcheck.go @@ -13,8 +13,19 @@ import ( "gorm.io/gorm" ) -func CreateHealthcheck(ctx context.Context, db *gorm.DB, healthcheck *models.Healthcheck) error { - return db.WithContext(ctx).Create(healthcheck).Error +func getScheduleId(healthcheck *models.Healthcheck, group string) string { + return "healthcheck-" + healthcheck.Slug + "-" + group +} + +func CreateHealthcheck(ctx context.Context, query *query.Query, healthcheck *models.Healthcheck) error { + return query.Healthcheck.WithContext(ctx).Create(healthcheck) +} + +func UpdateHealthcheck(ctx context.Context, q *query.Query, healthcheck *models.Healthcheck) error { + _, err := q.Healthcheck.WithContext(ctx).Where( + q.Healthcheck.Slug.Eq(healthcheck.Slug), + ).Updates(healthcheck) + return err } func GetHealthcheck(ctx context.Context, q *query.Query, slug string) (*models.Healthcheck, error) { @@ -34,33 +45,62 @@ func GetHealthchecks(ctx context.Context, q *query.Query) ([]*models.Healthcheck ).Find() } -func StartHealthcheck(ctx context.Context, t client.Client, healthcheck *models.Healthcheck) error { - log.Println("Starting Healthcheck Workflow") +func CreateOrUpdateHealthcheckSchedule(ctx context.Context, t client.Client, healthcheck *models.Healthcheck) error { + log.Println("Creating or Updating Healthcheck Schedule") args := make([]interface{}, 0) args = append(args, workflows.HealthcheckWorkflowParam{Script: healthcheck.Script, Slug: healthcheck.Slug}) - id := "healthcheck-" + healthcheck.Slug - - fakeWorkflows := workflows.NewWorkflows(nil) - for _, group := range healthcheck.WorkerGroups { - _, err := t.ScheduleClient().Create(ctx, client.ScheduleOptions{ - ID: id + "-" + group, + options := client.ScheduleOptions{ + ID: getScheduleId(healthcheck, group), + //SearchAttributes: map[string]interface{}{ + // "worker-group": group, + // "healthcheck-slug": healthcheck.Slug, + //}, Spec: client.ScheduleSpec{ CronExpressions: []string{healthcheck.Schedule}, Jitter: time.Second * 10, }, Action: &client.ScheduleWorkflowAction{ - ID: id + "-" + group, - Workflow: fakeWorkflows.HealthcheckWorkflowDefinition, + ID: getScheduleId(healthcheck, group), + Workflow: workflows.NewWorkflows(nil).HealthcheckWorkflowDefinition, Args: args, TaskQueue: group, RetryPolicy: &temporal.RetryPolicy{ MaximumAttempts: 3, }, }, - }) + } + + schedule := t.ScheduleClient().GetHandle(ctx, getScheduleId(healthcheck, group)) + + // If exists, we update + _, err := schedule.Describe(ctx) + if err == nil { + err = schedule.Update(ctx, client.ScheduleUpdateOptions{ + DoUpdate: func(input client.ScheduleUpdateInput) (*client.ScheduleUpdate, error) { + return &client.ScheduleUpdate{ + Schedule: &client.Schedule{ + Spec: &options.Spec, + Action: options.Action, + Policy: input.Description.Schedule.Policy, + State: input.Description.Schedule.State, + }, + }, nil + }, + }) + if err != nil { + return err + } + } else { + schedule, err = t.ScheduleClient().Create(ctx, options) + if err != nil { + return err + } + } + + err = schedule.Trigger(ctx, client.ScheduleTriggerOptions{}) if err != nil { return err } diff --git a/internal/services/worker.go b/internal/services/worker.go index dda1188..a6d25a0 100644 --- a/internal/services/worker.go +++ b/internal/services/worker.go @@ -6,11 +6,10 @@ import ( "code.tjo.space/mentos1386/zdravko/internal/models" "code.tjo.space/mentos1386/zdravko/internal/models/query" - "gorm.io/gorm" ) -func CreateWorker(ctx context.Context, db *gorm.DB, worker *models.Worker) error { - return db.WithContext(ctx).Create(worker).Error +func CreateWorker(ctx context.Context, q *query.Query, worker *models.Worker) error { + return q.Worker.WithContext(ctx).Create(worker) } func GetWorker(ctx context.Context, q *query.Query, slug string) (*models.Worker, error) { diff --git a/pkg/server/server.go b/pkg/server/server.go index 1cd230e..6b862f9 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -48,7 +48,7 @@ func (s *Server) Start() error { } log.Println("Connected to Temporal") - h := handlers.NewBaseHandler(db, query, temporalClient, s.cfg) + h := handlers.NewBaseHandler(query, temporalClient, s.cfg) // Health s.echo.GET("/health", func(c echo.Context) error { @@ -82,6 +82,7 @@ func (s *Server) Start() error { settings.GET("/healthchecks/create", h.SettingsHealthchecksCreateGET) settings.POST("/healthchecks/create", h.SettingsHealthchecksCreatePOST) settings.GET("/healthchecks/:slug", h.SettingsHealthchecksDescribeGET) + settings.POST("/healthchecks/:slug", h.SettingsHealthchecksDescribePOST) settings.GET("/workers", h.SettingsWorkersGET) settings.GET("/workers/create", h.SettingsWorkersCreateGET) settings.POST("/workers/create", h.SettingsWorkersCreatePOST) diff --git a/web/static/css/tailwind.css b/web/static/css/tailwind.css index 63eee69..9891ab8 100644 --- a/web/static/css/tailwind.css +++ b/web/static/css/tailwind.css @@ -912,6 +912,11 @@ video { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } +.bg-yellow-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 249 195 / var(--tw-bg-opacity)); +} + .p-2 { padding: 0.5rem; } @@ -1124,6 +1129,11 @@ video { color: rgb(255 255 255 / var(--tw-text-opacity)); } +.text-yellow-800 { + --tw-text-opacity: 1; + color: rgb(133 77 14 / var(--tw-text-opacity)); +} + .underline { text-decoration-line: underline; } diff --git a/web/templates/pages/settings_healthchecks.tmpl b/web/templates/pages/settings_healthchecks.tmpl index b8403a2..9c772e9 100644 --- a/web/templates/pages/settings_healthchecks.tmpl +++ b/web/templates/pages/settings_healthchecks.tmpl @@ -66,13 +66,10 @@