feat(healthcheck): use slugs and actual params

This commit is contained in:
Tine 2024-02-16 13:41:18 +01:00
parent 224f1f93fa
commit e6f6e5ede3
Signed by: mentos1386
SSH key fingerprint: SHA256:MNtTsLbihYaWF8j1fkOHfkKNlnN1JQfxEU/rBU8nCGw
13 changed files with 76 additions and 40 deletions

View file

@ -56,7 +56,7 @@ func main() {
r.HandleFunc("/settings/healthchecks", h.Authenticated(h.SettingsHealthchecksGET)).Methods("GET")
r.HandleFunc("/settings/healthchecks/create", h.Authenticated(h.SettingsHealthchecksCreateGET)).Methods("GET")
r.HandleFunc("/settings/healthchecks/create", h.Authenticated(h.SettingsHealthchecksCreatePOST)).Methods("POST")
r.HandleFunc("/settings/healthchecks/{id}", h.Authenticated(h.SettingsHealthchecksDescribeGET)).Methods("GET")
r.HandleFunc("/settings/healthchecks/{slug}", h.Authenticated(h.SettingsHealthchecksDescribeGET)).Methods("GET")
// OAuth2
r.HandleFunc("/oauth2/login", h.OAuth2LoginGET).Methods("GET")

2
go.mod
View file

@ -60,6 +60,8 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gosimple/slug v1.13.1 // indirect
github.com/gosimple/unidecode v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect

4
go.sum
View file

@ -192,6 +192,10 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q=
github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=

View file

@ -2,6 +2,7 @@ package activities
import (
"context"
"log"
"net/http"
)
@ -11,7 +12,7 @@ type HealtcheckHttpActivityParam struct {
}
type HealthcheckHttpActivityResult struct {
Success bool
StatusCode int
}
func HealthcheckHttpActivityDefinition(ctx context.Context, param HealtcheckHttpActivityParam) (*HealthcheckHttpActivityResult, error) {
@ -35,5 +36,7 @@ func HealthcheckHttpActivityDefinition(ctx context.Context, param HealtcheckHttp
return nil, err
}
return &HealthcheckHttpActivityResult{Success: response.StatusCode == 200}, nil
log.Printf("HealthcheckHttpActivityDefinition produced statuscode %d for url %s", response.StatusCode, param.Url)
return &HealthcheckHttpActivityResult{StatusCode: response.StatusCode}, nil
}

View file

@ -3,9 +3,7 @@ package handlers
import (
"context"
"fmt"
"log"
"net/http"
"strconv"
"text/template"
"code.tjo.space/mentos1386/zdravko/internal/models"
@ -13,6 +11,7 @@ import (
"code.tjo.space/mentos1386/zdravko/web/templates"
"code.tjo.space/mentos1386/zdravko/web/templates/components"
"github.com/gorilla/mux"
"github.com/gosimple/slug"
)
type SettingsHealthchecks struct {
@ -58,10 +57,7 @@ func (h *BaseHandler) SettingsHealthchecksGET(w http.ResponseWriter, r *http.Req
func (h *BaseHandler) SettingsHealthchecksDescribeGET(w http.ResponseWriter, r *http.Request, user *AuthenticatedUser) {
vars := mux.Vars(r)
id, err := strconv.ParseUint(vars["id"], 10, 32)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
slug := vars["slug"]
ts, err := template.ParseFS(templates.Templates,
"components/base.tmpl",
@ -73,7 +69,7 @@ func (h *BaseHandler) SettingsHealthchecksDescribeGET(w http.ResponseWriter, r *
return
}
healthcheck, err := services.GetHealthcheckHttp(context.Background(), h.query, uint(id))
healthcheck, err := services.GetHealthcheckHttp(context.Background(), h.query, slug)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
@ -85,7 +81,7 @@ func (h *BaseHandler) SettingsHealthchecksDescribeGET(w http.ResponseWriter, r *
[]*components.Page{
GetPageByTitle(SettingsPages, "Healthchecks"),
{
Path: fmt.Sprintf("/settings/healthchecks/%d", id),
Path: fmt.Sprintf("/settings/healthchecks/%s", slug),
Title: "Describe",
Breadcrumb: healthcheck.Name,
},
@ -124,24 +120,27 @@ func (h *BaseHandler) SettingsHealthchecksCreateGET(w http.ResponseWriter, r *ht
func (h *BaseHandler) SettingsHealthchecksCreatePOST(w http.ResponseWriter, r *http.Request, user *AuthenticatedUser) {
ctx := context.Background()
healthcheckHttp := &models.HealthcheckHttp{
Healthcheck: models.Healthcheck{
Name: r.FormValue("name"),
Slug: slug.Make(r.FormValue("name")),
Schedule: r.FormValue("schedule"),
},
Url: r.FormValue("url"),
Method: r.FormValue("method"),
}
err := services.CreateHealthcheckHttp(
ctx,
h.db,
&models.HealthcheckHttp{
Healthcheck: models.Healthcheck{
Name: r.FormValue("name"),
Schedule: r.FormValue("schedule"),
},
URL: r.FormValue("url"),
Method: r.FormValue("method"),
})
healthcheckHttp,
)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
err = services.StartHealthcheckHttp(ctx, h.temporal)
err = services.StartHealthcheckHttp(ctx, h.temporal, healthcheckHttp)
if err != nil {
log.Println("Error starting healthcheck workflow", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}

View file

@ -13,6 +13,7 @@ type OAuth2State struct {
type Healthcheck struct {
gorm.Model
Slug string `gorm:"unique"`
Name string `gorm:"unique"`
Status string // UP, DOWN
UptimePercentage float64
@ -22,7 +23,7 @@ type Healthcheck struct {
type HealthcheckHttp struct {
gorm.Model
Healthcheck
URL string
Url string
Method string
}
@ -35,6 +36,7 @@ type HealthcheckTcp struct {
type Cronjob struct {
gorm.Model
Slug string `gorm:"unique"`
Name string `gorm:"unique"`
Schedule string
Buffer int

View file

@ -31,6 +31,7 @@ func newCronjob(db *gorm.DB, opts ...gen.DOOption) cronjob {
_cronjob.CreatedAt = field.NewTime(tableName, "created_at")
_cronjob.UpdatedAt = field.NewTime(tableName, "updated_at")
_cronjob.DeletedAt = field.NewField(tableName, "deleted_at")
_cronjob.Slug = field.NewString(tableName, "slug")
_cronjob.Name = field.NewString(tableName, "name")
_cronjob.Schedule = field.NewString(tableName, "schedule")
_cronjob.Buffer = field.NewInt(tableName, "buffer")
@ -48,6 +49,7 @@ type cronjob struct {
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
Slug field.String
Name field.String
Schedule field.String
Buffer field.Int
@ -71,6 +73,7 @@ func (c *cronjob) updateTableName(table string) *cronjob {
c.CreatedAt = field.NewTime(table, "created_at")
c.UpdatedAt = field.NewTime(table, "updated_at")
c.DeletedAt = field.NewField(table, "deleted_at")
c.Slug = field.NewString(table, "slug")
c.Name = field.NewString(table, "name")
c.Schedule = field.NewString(table, "schedule")
c.Buffer = field.NewInt(table, "buffer")
@ -98,11 +101,12 @@ func (c *cronjob) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (c *cronjob) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 7)
c.fieldMap = make(map[string]field.Expr, 8)
c.fieldMap["id"] = c.ID
c.fieldMap["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt
c.fieldMap["deleted_at"] = c.DeletedAt
c.fieldMap["slug"] = c.Slug
c.fieldMap["name"] = c.Name
c.fieldMap["schedule"] = c.Schedule
c.fieldMap["buffer"] = c.Buffer

View file

@ -31,11 +31,12 @@ func newHealthcheckHttp(db *gorm.DB, opts ...gen.DOOption) healthcheckHttp {
_healthcheckHttp.CreatedAt = field.NewTime(tableName, "created_at")
_healthcheckHttp.UpdatedAt = field.NewTime(tableName, "updated_at")
_healthcheckHttp.DeletedAt = field.NewField(tableName, "deleted_at")
_healthcheckHttp.Slug = field.NewString(tableName, "slug")
_healthcheckHttp.Name = field.NewString(tableName, "name")
_healthcheckHttp.Status = field.NewString(tableName, "status")
_healthcheckHttp.UptimePercentage = field.NewFloat64(tableName, "uptime_percentage")
_healthcheckHttp.Schedule = field.NewString(tableName, "schedule")
_healthcheckHttp.URL = field.NewString(tableName, "url")
_healthcheckHttp.Url = field.NewString(tableName, "url")
_healthcheckHttp.Method = field.NewString(tableName, "method")
_healthcheckHttp.fillFieldMap()
@ -51,11 +52,12 @@ type healthcheckHttp struct {
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
Slug field.String
Name field.String
Status field.String
UptimePercentage field.Float64
Schedule field.String
URL field.String
Url field.String
Method field.String
fieldMap map[string]field.Expr
@ -77,11 +79,12 @@ func (h *healthcheckHttp) updateTableName(table string) *healthcheckHttp {
h.CreatedAt = field.NewTime(table, "created_at")
h.UpdatedAt = field.NewTime(table, "updated_at")
h.DeletedAt = field.NewField(table, "deleted_at")
h.Slug = field.NewString(table, "slug")
h.Name = field.NewString(table, "name")
h.Status = field.NewString(table, "status")
h.UptimePercentage = field.NewFloat64(table, "uptime_percentage")
h.Schedule = field.NewString(table, "schedule")
h.URL = field.NewString(table, "url")
h.Url = field.NewString(table, "url")
h.Method = field.NewString(table, "method")
h.fillFieldMap()
@ -111,16 +114,17 @@ func (h *healthcheckHttp) GetFieldByName(fieldName string) (field.OrderExpr, boo
}
func (h *healthcheckHttp) fillFieldMap() {
h.fieldMap = make(map[string]field.Expr, 10)
h.fieldMap = make(map[string]field.Expr, 11)
h.fieldMap["id"] = h.ID
h.fieldMap["created_at"] = h.CreatedAt
h.fieldMap["updated_at"] = h.UpdatedAt
h.fieldMap["deleted_at"] = h.DeletedAt
h.fieldMap["slug"] = h.Slug
h.fieldMap["name"] = h.Name
h.fieldMap["status"] = h.Status
h.fieldMap["uptime_percentage"] = h.UptimePercentage
h.fieldMap["schedule"] = h.Schedule
h.fieldMap["url"] = h.URL
h.fieldMap["url"] = h.Url
h.fieldMap["method"] = h.Method
}

View file

@ -31,6 +31,7 @@ func newHealthcheckTcp(db *gorm.DB, opts ...gen.DOOption) healthcheckTcp {
_healthcheckTcp.CreatedAt = field.NewTime(tableName, "created_at")
_healthcheckTcp.UpdatedAt = field.NewTime(tableName, "updated_at")
_healthcheckTcp.DeletedAt = field.NewField(tableName, "deleted_at")
_healthcheckTcp.Slug = field.NewString(tableName, "slug")
_healthcheckTcp.Name = field.NewString(tableName, "name")
_healthcheckTcp.Status = field.NewString(tableName, "status")
_healthcheckTcp.UptimePercentage = field.NewFloat64(tableName, "uptime_percentage")
@ -51,6 +52,7 @@ type healthcheckTcp struct {
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
Slug field.String
Name field.String
Status field.String
UptimePercentage field.Float64
@ -77,6 +79,7 @@ func (h *healthcheckTcp) updateTableName(table string) *healthcheckTcp {
h.CreatedAt = field.NewTime(table, "created_at")
h.UpdatedAt = field.NewTime(table, "updated_at")
h.DeletedAt = field.NewField(table, "deleted_at")
h.Slug = field.NewString(table, "slug")
h.Name = field.NewString(table, "name")
h.Status = field.NewString(table, "status")
h.UptimePercentage = field.NewFloat64(table, "uptime_percentage")
@ -111,11 +114,12 @@ func (h *healthcheckTcp) GetFieldByName(fieldName string) (field.OrderExpr, bool
}
func (h *healthcheckTcp) fillFieldMap() {
h.fieldMap = make(map[string]field.Expr, 10)
h.fieldMap = make(map[string]field.Expr, 11)
h.fieldMap["id"] = h.ID
h.fieldMap["created_at"] = h.CreatedAt
h.fieldMap["updated_at"] = h.UpdatedAt
h.fieldMap["deleted_at"] = h.DeletedAt
h.fieldMap["slug"] = h.Slug
h.fieldMap["name"] = h.Name
h.fieldMap["status"] = h.Status
h.fieldMap["uptime_percentage"] = h.UptimePercentage

View file

@ -8,6 +8,7 @@ import (
"code.tjo.space/mentos1386/zdravko/internal/models/query"
"code.tjo.space/mentos1386/zdravko/internal/workflows"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/temporal"
"gorm.io/gorm"
)
@ -15,29 +16,32 @@ func CreateHealthcheckHttp(ctx context.Context, db *gorm.DB, healthcheck *models
return db.WithContext(ctx).Create(healthcheck).Error
}
func GetHealthcheckHttp(ctx context.Context, q *query.Query, id uint) (*models.HealthcheckHttp, error) {
func GetHealthcheckHttp(ctx context.Context, q *query.Query, slug string) (*models.HealthcheckHttp, error) {
log.Println("GetHealthcheckHttp")
return q.HealthcheckHttp.WithContext(ctx).Where(
q.HealthcheckHttp.ID.Eq(id),
q.HealthcheckHttp.Slug.Eq(slug),
).First()
}
func StartHealthcheckHttp(ctx context.Context, t client.Client) error {
func StartHealthcheckHttp(ctx context.Context, t client.Client, healthcheckHttp *models.HealthcheckHttp) error {
log.Println("Starting HealthcheckHttp Workflow")
args := make([]interface{}, 0)
args = append(args, workflows.HealthcheckHttpWorkflowParam{Id: 1})
args = append(args, workflows.HealthcheckHttpWorkflowParam{Url: healthcheckHttp.Url, Method: healthcheckHttp.Method})
_, err := t.ScheduleClient().Create(ctx, client.ScheduleOptions{
ID: "healthcheck-http-id",
ID: "healthcheck-http-" + healthcheckHttp.Slug,
Spec: client.ScheduleSpec{
CronExpressions: []string{"0 * * * *"},
CronExpressions: []string{healthcheckHttp.Schedule},
},
Action: &client.ScheduleWorkflowAction{
ID: "healthcheck-http-id-workflow",
Workflow: workflows.HealthcheckHttpWorkflowDefinition,
Args: args,
TaskQueue: "default",
RetryPolicy: &temporal.RetryPolicy{
MaximumAttempts: 3,
},
},
})

View file

@ -1,6 +1,7 @@
package workflows
import (
"fmt"
"time"
"code.tjo.space/mentos1386/zdravko/internal/activities"
@ -8,7 +9,8 @@ import (
)
type HealthcheckHttpWorkflowParam struct {
Id uint
Url string
Method string
}
func HealthcheckHttpWorkflowDefinition(ctx workflow.Context, param HealthcheckHttpWorkflowParam) error {
@ -18,12 +20,19 @@ func HealthcheckHttpWorkflowDefinition(ctx workflow.Context, param HealthcheckHt
ctx = workflow.WithActivityOptions(ctx, options)
activityParam := activities.HealtcheckHttpActivityParam{
Url: "https://google.com",
Method: "GET",
Url: param.Url,
Method: param.Method,
}
var result *activities.HealthcheckHttpActivityResult
err := workflow.ExecuteActivity(ctx, activities.HealthcheckHttpActivityDefinition, activityParam).Get(ctx, &result)
if err != nil {
return err
}
return err
if result.StatusCode != 200 {
return fmt.Errorf("HealthcheckHttpActivityDefinition produced statuscode %d for url %s", result.StatusCode, param.Url)
}
return nil
}

View file

@ -70,7 +70,7 @@
{{.Schedule}}
</td>
<td class="px-6 py-4">
<a href="/settings/healthchecks/{{.ID}}" class="font-medium text-blue-600 hover:underline">Details</a>
<a href="/settings/healthchecks/{{.Slug}}" class="font-medium text-blue-600 hover:underline">Details</a>
</td>
</tr>
</tbody>

View file

@ -4,6 +4,7 @@
{{ .Healthcheck.Name }}
</h1>
{{ .Healthcheck.ID }}
{{ .Healthcheck.Slug }}
{{ .Healthcheck.URL }}
{{ .Healthcheck.Schedule }}
</section>