diff --git a/database/models/models.go b/database/models/models.go index 4de1c50..cf0e9eb 100644 --- a/database/models/models.go +++ b/database/models/models.go @@ -142,7 +142,10 @@ const ( type TargetHistory struct { CreatedAt *Time `db:"created_at"` - TargetId string `db:"target_id"` - Status TargetStatus `db:"status"` - Note string `db:"note"` + TargetId string `db:"target_id"` + WorkerGroupId string `db:"worker_group_id"` + CheckId string `db:"check_id"` + + Status TargetStatus `db:"status"` + Note string `db:"note"` } diff --git a/database/sqlite/migrations/2024-02-27-initial.sql b/database/sqlite/migrations/2024-02-27-initial.sql index eeded5d..66a7fa7 100644 --- a/database/sqlite/migrations/2024-02-27-initial.sql +++ b/database/sqlite/migrations/2024-02-27-initial.sql @@ -92,15 +92,19 @@ END; -- +migrate StatementEnd CREATE TABLE target_histories ( - target_id TEXT NOT NULL, + target_id TEXT NOT NULL, + worker_group_id TEXT NOT NULL, + check_id TEXT NOT NULL, status TEXT NOT NULL, note TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ')), - PRIMARY KEY (target_id, created_at), - CONSTRAINT fk_target_histories_target FOREIGN KEY (target_id) REFERENCES targets(id) ON DELETE CASCADE + PRIMARY KEY (target_id, worker_group_id, check_id, created_at), + CONSTRAINT fk_target_histories_target FOREIGN KEY (target_id) REFERENCES targets(id) ON DELETE CASCADE, + CONSTRAINT fk_target_histories_worker_group FOREIGN KEY (worker_group_id) REFERENCES worker_groups(id) ON DELETE CASCADE, + CONSTRAINT fk_target_histories_check FOREIGN KEY (check_id) REFERENCES checks(id) ON DELETE CASCADE ) STRICT; -- +migrate Down diff --git a/internal/server/activities/add_target_history.go b/internal/server/activities/add_target_history.go index 682f069..6973695 100644 --- a/internal/server/activities/add_target_history.go +++ b/internal/server/activities/add_target_history.go @@ -19,9 +19,11 @@ func (a *Activities) AddTargetHistory(ctx context.Context, param temporal.Activi } err := services.AddHistoryForTarget(ctx, a.db, &models.TargetHistory{ - TargetId: param.Target.Id, - Status: status, - Note: param.Note, + TargetId: param.Target.Id, + WorkerGroupId: param.WorkerGroupId, + CheckId: param.CheckId, + Status: status, + Note: param.Note, }) return &temporal.ActivityAddTargetHistoryResult{}, err diff --git a/internal/server/handlers/settings_checks.go b/internal/server/handlers/settings_checks.go index 65b317c..933a3c4 100644 --- a/internal/server/handlers/settings_checks.go +++ b/internal/server/handlers/settings_checks.go @@ -71,7 +71,8 @@ func (h *BaseHandler) SettingsChecksGET(c echo.Context) error { for i, check := range checks { state, err := services.GetCheckState(context.Background(), h.temporal, check.Id) if err != nil { - return err + h.logger.Error("Failed to get check state", "error", err) + state = models.CheckStateUnknown } checksWithState[i] = &CheckWithWorkerGroupsAndState{ CheckWithWorkerGroups: check, diff --git a/internal/server/services/check.go b/internal/server/services/check.go index 67c7473..4b88ad9 100644 --- a/internal/server/services/check.go +++ b/internal/server/services/check.go @@ -267,7 +267,7 @@ func CreateOrUpdateCheckSchedule( ID: getScheduleId(check.Id), Workflow: internaltemporal.WorkflowCheckName, Args: args, - TaskQueue: "default", + TaskQueue: internaltemporal.TEMPORAL_SERVER_QUEUE, RetryPolicy: &temporal.RetryPolicy{ MaximumAttempts: 3, }, diff --git a/internal/server/services/check_history.go b/internal/server/services/check_history.go index cbb747b..c7ac6cb 100644 --- a/internal/server/services/check_history.go +++ b/internal/server/services/check_history.go @@ -5,20 +5,26 @@ import ( "fmt" "time" + "github.com/mentos1386/zdravko/internal/temporal" + "go.temporal.io/api/enums/v1" "go.temporal.io/api/workflowservice/v1" "go.temporal.io/sdk/client" ) type CheckHistory struct { - CheckId string - Status string - Duration time.Duration + CheckId string + Status string + Duration time.Duration + StartTime time.Time + EndTime time.Time + WorkerGroupName string + Note string } -func GetLastNCheckHistory(ctx context.Context, temporal client.Client, n int32) ([]*CheckHistory, error) { +func GetLastNCheckHistory(ctx context.Context, t client.Client, n int32) ([]*CheckHistory, error) { var checkHistory []*CheckHistory - response, err := temporal.ListWorkflow(ctx, &workflowservice.ListWorkflowExecutionsRequest{ + response, err := t.ListWorkflow(ctx, &workflowservice.ListWorkflowExecutionsRequest{ PageSize: n, }) if err != nil { @@ -29,21 +35,37 @@ func GetLastNCheckHistory(ctx context.Context, temporal client.Client, n int32) for _, execution := range executions { scheduleId := string(execution.GetSearchAttributes().GetIndexedFields()["TemporalScheduledById"].Data) - checkId := scheduleId[len("check-"):] + + // Remove the quotes around the checkId and the prefix. + checkId := scheduleId[len("\"check-") : len(scheduleId)-1] + + var result temporal.WorkflowCheckResult + if execution.Status != enums.WORKFLOW_EXECUTION_STATUS_RUNNING { + workflowRun := t.GetWorkflow(ctx, execution.GetExecution().GetWorkflowId(), execution.GetExecution().GetRunId()) + err := workflowRun.Get(ctx, &result) + if err != nil { + return nil, err + } + } + checkHistory = append(checkHistory, &CheckHistory{ - CheckId: checkId, - Duration: execution.CloseTime.AsTime().Sub(execution.StartTime.AsTime()), - Status: execution.Status.String(), + CheckId: checkId, + Duration: execution.CloseTime.AsTime().Sub(execution.StartTime.AsTime()), + StartTime: execution.StartTime.AsTime(), + EndTime: execution.CloseTime.AsTime(), + Status: execution.Status.String(), + WorkerGroupName: execution.GetTaskQueue(), + Note: result.Note, }) } return checkHistory, nil } -func GetCheckHistoryForCheck(ctx context.Context, temporal client.Client, checkId string) ([]*CheckHistory, error) { +func GetCheckHistoryForCheck(ctx context.Context, t client.Client, checkId string) ([]*CheckHistory, error) { var checkHistory []*CheckHistory - response, err := temporal.ListWorkflow(ctx, &workflowservice.ListWorkflowExecutionsRequest{ + response, err := t.ListWorkflow(ctx, &workflowservice.ListWorkflowExecutionsRequest{ PageSize: 10, Query: fmt.Sprintf(`TemporalScheduledById = "%s"`, getScheduleId(checkId)), }) @@ -55,11 +77,27 @@ func GetCheckHistoryForCheck(ctx context.Context, temporal client.Client, checkI for _, execution := range executions { scheduleId := string(execution.GetSearchAttributes().GetIndexedFields()["TemporalScheduledById"].Data) - checkId := scheduleId[len("check-"):] + + // Remove the quotes around the checkId and the prefix. + checkId := scheduleId[len("\"check-") : len(scheduleId)-1] + + var result temporal.WorkflowCheckResult + if execution.Status != enums.WORKFLOW_EXECUTION_STATUS_RUNNING { + workflowRun := t.GetWorkflow(ctx, execution.GetExecution().GetWorkflowId(), execution.GetExecution().GetRunId()) + err := workflowRun.Get(ctx, &result) + if err != nil { + return nil, err + } + } + checkHistory = append(checkHistory, &CheckHistory{ - CheckId: checkId, - Duration: execution.CloseTime.AsTime().Sub(execution.StartTime.AsTime()), - Status: execution.Status.String(), + CheckId: checkId, + Duration: execution.CloseTime.AsTime().Sub(execution.StartTime.AsTime()), + StartTime: execution.StartTime.AsTime(), + EndTime: execution.CloseTime.AsTime(), + Status: execution.Status.String(), + WorkerGroupName: execution.GetTaskQueue(), + Note: result.Note, }) } diff --git a/internal/server/services/targets_history.go b/internal/server/services/targets_history.go index 9f5046e..e233fd2 100644 --- a/internal/server/services/targets_history.go +++ b/internal/server/services/targets_history.go @@ -3,13 +3,15 @@ package services import ( "context" - "github.com/mentos1386/zdravko/database/models" "github.com/jmoiron/sqlx" + "github.com/mentos1386/zdravko/database/models" ) type TargetHistory struct { *models.TargetHistory - TargetName string `db:"target_name"` + TargetName string `db:"target_name"` + WorkerGroupName string `db:"worker_group_name"` + CheckName string `db:"check_name"` } func GetLastNTargetHistory(ctx context.Context, db *sqlx.DB, n int) ([]*TargetHistory, error) { @@ -17,9 +19,13 @@ func GetLastNTargetHistory(ctx context.Context, db *sqlx.DB, n int) ([]*TargetHi err := db.SelectContext(ctx, &targetHistory, ` SELECT th.*, - t.name AS target_name + t.name AS target_name, + wg.name AS worker_group_name, + c.name AS check_name FROM target_histories th LEFT JOIN targets t ON th.target_id = t.id + LEFT JOIN worker_groups wg ON th.worker_group_id = wg.id + LEFT JOIN checks c ON th.check_id = c.id WHERE th.target_id = $1 ORDER BY th.created_at DESC LIMIT $1 @@ -32,9 +38,13 @@ func GetTargetHistoryForTarget(ctx context.Context, db *sqlx.DB, targetId string err := db.SelectContext(ctx, &targetHistory, ` SELECT th.*, - t.name AS target_name + t.name AS target_name, + wg.name AS worker_group_name, + c.name AS check_name FROM target_histories th LEFT JOIN targets t ON th.target_id = t.id + LEFT JOIN worker_groups wg ON th.worker_group_id = wg.id + LEFT JOIN checks c ON th.check_id = c.id WHERE th.target_id = $1 ORDER BY th.created_at DESC `, targetId) @@ -46,10 +56,14 @@ func AddHistoryForTarget(ctx context.Context, db *sqlx.DB, history *models.Targe ` INSERT INTO target_histories ( target_id, + worker_group_id, + check_id, status, note ) VALUES ( :target_id, + :worker_group_id, + :check_id, :status, :note )`, diff --git a/internal/server/workflows/check.go b/internal/server/workflows/check.go index afaedec..6ab4505 100644 --- a/internal/server/workflows/check.go +++ b/internal/server/workflows/check.go @@ -1,7 +1,6 @@ package workflows import ( - "log/slog" "sort" "time" @@ -9,7 +8,7 @@ import ( "go.temporal.io/sdk/workflow" ) -func (w *Workflows) CheckWorkflowDefinition(ctx workflow.Context, param temporal.WorkflowCheckParam) error { +func (w *Workflows) CheckWorkflowDefinition(ctx workflow.Context, param temporal.WorkflowCheckParam) (*temporal.WorkflowCheckResult, error) { workerGroupIds := param.WorkerGroupIds sort.Strings(workerGroupIds) @@ -25,7 +24,7 @@ func (w *Workflows) CheckWorkflowDefinition(ctx workflow.Context, param temporal }, ).Get(ctx, &targetsFilterResult) if err != nil { - return err + return nil, err } for _, target := range targetsFilterResult.Targets { @@ -43,7 +42,7 @@ func (w *Workflows) CheckWorkflowDefinition(ctx workflow.Context, param temporal }, ).Get(ctx, &checkResult) if err != nil { - return err + return nil, err } status := temporal.AddTargetHistoryStatusFailure @@ -59,18 +58,20 @@ func (w *Workflows) CheckWorkflowDefinition(ctx workflow.Context, param temporal }), temporal.ActivityAddTargetHistoryName, &temporal.ActivityAddTargetHistoryParam{ - Target: target, - Status: status, - Note: checkResult.Note, + Target: target, + WorkerGroupId: workerGroupId, + CheckId: param.CheckId, + Status: status, + Note: checkResult.Note, }, ).Get(ctx, &addTargetHistoryResult) if err != nil { - return err + return nil, err } - - slog.Info("Check %s status: %s", param.CheckId, status) } } - return nil + return &temporal.WorkflowCheckResult{ + Note: "Check workflow completed", + }, nil } diff --git a/internal/temporal/activity_add_target_history.go b/internal/temporal/activity_add_target_history.go index ad930cf..5c7ae6f 100644 --- a/internal/temporal/activity_add_target_history.go +++ b/internal/temporal/activity_add_target_history.go @@ -9,9 +9,11 @@ const ( ) type ActivityAddTargetHistoryParam struct { - Target *Target - Status AddTargetHistoryStatus - Note string + Target *Target + WorkerGroupId string + CheckId string + Status AddTargetHistoryStatus + Note string } type ActivityAddTargetHistoryResult struct { diff --git a/internal/temporal/workflow_check.go b/internal/temporal/workflow_check.go index c4ac178..25ca2ad 100644 --- a/internal/temporal/workflow_check.go +++ b/internal/temporal/workflow_check.go @@ -7,4 +7,8 @@ type WorkflowCheckParam struct { WorkerGroupIds []string } +type WorkflowCheckResult struct { + Note string +} + const WorkflowCheckName = "CHECK_WORKFLOW" diff --git a/web/static/css/tailwind.css b/web/static/css/tailwind.css index 33257a3..aff86a8 100644 --- a/web/static/css/tailwind.css +++ b/web/static/css/tailwind.css @@ -991,6 +991,11 @@ video { background-color: rgb(253 186 116 / var(--tw-bg-opacity)); } +.bg-purple-100 { + --tw-bg-opacity: 1; + background-color: rgb(243 232 255 / var(--tw-bg-opacity)); +} + .bg-red-100 { --tw-bg-opacity: 1; background-color: rgb(254 226 226 / var(--tw-bg-opacity)); @@ -1225,6 +1230,11 @@ video { color: rgb(22 101 52 / var(--tw-text-opacity)); } +.text-purple-800 { + --tw-text-opacity: 1; + color: rgb(107 33 168 / var(--tw-text-opacity)); +} + .text-red-600 { --tw-text-opacity: 1; color: rgb(220 38 38 / var(--tw-text-opacity)); diff --git a/web/templates/pages/settings_checks_describe.tmpl b/web/templates/pages/settings_checks_describe.tmpl index a05ebc3..f78d0bd 100644 --- a/web/templates/pages/settings_checks_describe.tmpl +++ b/web/templates/pages/settings_checks_describe.tmpl @@ -118,7 +118,8 @@ Check ID Status Worker Group - Created At + Started At + Ended At Duration Note @@ -130,36 +131,35 @@ {{ .CheckId }} - {{ .Status }} + {{ .Status }}... - { .WorkerGroupName } + {{ .WorkerGroupName }} - { .CreatedAt.Time.Format "2006-01-02 15:04:05" } - / - / + {{ .StartTime.Format "2006-01-02 15:04:05" }} + + + {{ else }} {{ .CheckId }} {{ .Status }} @@ -168,12 +168,13 @@ - { .WorkerGroupName } + {{ .WorkerGroupName }} - { .CreatedAt.Time.Format "2006-01-02 15:04:05" } - {{ .Duration }} - { .Note } + {{ .StartTime.Format "2006-01-02 15:04:05" }} + {{ .EndTime.Format "2006-01-02 15:04:05" }} + {{ DurationRoundMillisecond .Duration }} + {{ .Note }} {{ end }} {{ end }} diff --git a/web/templates/pages/settings_home.tmpl b/web/templates/pages/settings_home.tmpl index d45b105..c816542 100644 --- a/web/templates/pages/settings_home.tmpl +++ b/web/templates/pages/settings_home.tmpl @@ -42,52 +42,76 @@ -
+
- - + - + + + + {{ range .History }} - - - - - - - + {{ if eq .Status "Running" }} + + + + + + + + + + {{ else }} + + + + + + + + + + {{ end }} {{ end }}
- Execution History -

Last 10 executions for all checks and worker groups.

+ History +

Last 10 executions.

CheckWorker GroupCheck ID StatusExecuted AtWorker GroupStarted AtEnded AtDuration Note
- {{ .CheckId }} - - - { .WorkerGroupName } - - - - {{ .Status }} - - { .CreatedAt.Time.Format "2006-01-02 15:04:05" }{ .Note }
{{ .CheckId }} + + {{ .Status }}... + + + + {{ .WorkerGroupName }} + + {{ .StartTime.Format "2006-01-02 15:04:05" }}
{{ .CheckId }} + + {{ .Status }} + + + + {{ .WorkerGroupName }} + + {{ .StartTime.Format "2006-01-02 15:04:05" }}{{ .EndTime.Format "2006-01-02 15:04:05" }}{{ DurationRoundMillisecond .Duration }}{{ .Note }}
diff --git a/web/templates/pages/settings_targets_describe.tmpl b/web/templates/pages/settings_targets_describe.tmpl index 9aae8ec..55090a1 100644 --- a/web/templates/pages/settings_targets_describe.tmpl +++ b/web/templates/pages/settings_targets_describe.tmpl @@ -116,6 +116,7 @@ Status Worker Group + Check Created At Duration Note @@ -142,6 +143,13 @@ {{ .WorkerGroupName }} + + + {{ .CheckName }} + + {{ .CreatedAt.Time.Format "2006-01-02 15:04:05" }} diff --git a/web/templates/templates.go b/web/templates/templates.go index 39621e0..8eb7c4e 100644 --- a/web/templates/templates.go +++ b/web/templates/templates.go @@ -26,6 +26,12 @@ func load(files ...string) *template.Template { t := template.New("default").Funcs( template.FuncMap{ + "DurationRoundSecond": func(d time.Duration) time.Duration { + return d.Round(time.Second) + }, + "DurationRoundMillisecond": func(d time.Duration) time.Duration { + return d.Round(time.Millisecond) + }, "StringsJoin": strings.Join, "Now": time.Now, "ScriptUnescapeString": script.UnescapeString,