Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Structured task errors. #425

Merged
merged 5 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions addon/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,34 @@ func (h *Task) Succeeded() {
// Failed report addon failed.
// The reason can be a printf style format.
func (h *Task) Failed(reason string, x ...interface{}) {
reason = fmt.Sprintf(reason, x...)
h.report.Status = task.Failed
h.report.Error = fmt.Sprintf(reason, x...)
h.report.Errors = append(
h.report.Errors,
api.TaskError{
Severity: "Error",
Description: reason,
})
h.pushReport()
Log.Info(
"Addon reported: failed.",
"error",
h.report.Error)
"reason",
reason)
return
}

//
// Error report addon error.
// The description can be a printf style format.
func (h *Task) Error(severity, description string, x ...interface{}) {
description = fmt.Sprintf(description, x...)
h.report.Errors = append(
h.report.Errors,
api.TaskError{
Severity: severity,
Description: description,
})
h.pushReport()
return
}

Expand Down
23 changes: 18 additions & 5 deletions api/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,13 @@ type TTL struct {
Failed int `json:"failed,omitempty"`
}

//
// TaskError used in Task.Errors.
type TaskError struct {
Severity string `json:"severity"`
Description string `json:"description"`
}

//
// Task REST resource.
type Task struct {
Expand All @@ -515,7 +522,7 @@ type Task struct {
Purged bool `json:"purged,omitempty" yaml:",omitempty"`
Started *time.Time `json:"started,omitempty" yaml:",omitempty"`
Terminated *time.Time `json:"terminated,omitempty" yaml:",omitempty"`
Error string `json:"error,omitempty" yaml:",omitempty"`
Errors []TaskError `json:"errors,omitempty" yaml:",omitempty"`
Pod string `json:"pod,omitempty" yaml:",omitempty"`
Retries int `json:"retries,omitempty" yaml:",omitempty"`
Canceled bool `json:"canceled,omitempty" yaml:",omitempty"`
Expand All @@ -538,7 +545,6 @@ func (r *Task) With(m *model.Task) {
r.State = m.State
r.Started = m.Started
r.Terminated = m.Terminated
r.Error = m.Error
r.Pod = m.Pod
r.Retries = m.Retries
r.Canceled = m.Canceled
Expand All @@ -551,6 +557,9 @@ func (r *Task) With(m *model.Task) {
if m.TTL != nil {
_ = json.Unmarshal(m.TTL, &r.TTL)
}
if m.Errors != nil {
_ = json.Unmarshal(m.Errors, &r.Errors)
}
}

//
Expand Down Expand Up @@ -579,7 +588,7 @@ func (r *Task) Model() (m *model.Task) {
type TaskReport struct {
Resource `yaml:",inline"`
Status string `json:"status"`
Error string `json:"error,omitempty" yaml:",omitempty"`
Errors []TaskError `json:"errors,omitempty" yaml:",omitempty"`
Total int `json:"total,omitempty" yaml:",omitempty"`
Completed int `json:"completed,omitempty" yaml:",omitempty"`
Activity []string `json:"activity,omitempty" yaml:",omitempty"`
Expand All @@ -592,13 +601,15 @@ type TaskReport struct {
func (r *TaskReport) With(m *model.TaskReport) {
r.Resource.With(&m.Model)
r.Status = m.Status
r.Error = m.Error
r.Total = m.Total
r.Completed = m.Completed
r.TaskID = m.TaskID
if m.Activity != nil {
_ = json.Unmarshal(m.Activity, &r.Activity)
}
if m.Errors != nil {
_ = json.Unmarshal(m.Errors, &r.Errors)
}
if m.Result != nil {
_ = json.Unmarshal(m.Result, &r.Result)
}
Expand All @@ -612,7 +623,6 @@ func (r *TaskReport) Model() (m *model.TaskReport) {
}
m = &model.TaskReport{
Status: r.Status,
Error: r.Error,
Total: r.Total,
Completed: r.Completed,
TaskID: r.TaskID,
Expand All @@ -623,6 +633,9 @@ func (r *TaskReport) Model() (m *model.TaskReport) {
if r.Result != nil {
m.Result, _ = json.Marshal(r.Result)
}
if r.Errors != nil {
m.Errors, _ = json.Marshal(r.Errors)
}
m.ID = r.ID

return
Expand Down
5 changes: 5 additions & 0 deletions hack/add/analysis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ indirect: "true"
version: 4.6
" >> ${file}
echo -n "---
name: github.com/hybernate
indirect: "true"
version: 5.0
" >> ${file}
echo -n "---
name: github.com/ejb
indirect: "true"
version: 4.3
Expand Down
2 changes: 2 additions & 0 deletions hack/cmd/addon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ func main() {
}
return
})

addon.Error("Warning", "Test warning.")
}

//
Expand Down
68 changes: 68 additions & 0 deletions migration/v6/migrate.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package v6

import (
"encoding/json"
"github.com/jortel/go-utils/logr"
"github.com/konveyor/tackle2-hub/migration/v6/model"
"gorm.io/gorm"
Expand All @@ -21,9 +22,76 @@ func (r Migration) Apply(db *gorm.DB) (err error) {
return
}
err = db.AutoMigrate(r.Models()...)
if err != nil {
return
}
err = r.taskReportError(db)
if err != nil {
return
}
err = r.taskError(db)
if err != nil {
return
}
return
}

func (r Migration) Models() []interface{} {
return model.All()
}

func (r Migration) taskError(db *gorm.DB) (err error) {
type Task struct {
model.Task
Error string
}
var list []Task
err = db.Find(&Task{}, &list).Error
if err != nil {
return
}
for i := range list {
m := &list[i]
if m.Error == "" {
continue
}
m.Errors, _ = json.Marshal(
[]model.TaskError{
{
Severity: "Error",
Description: m.Error,
},
})
}
m := db.Migrator()
err = m.DropColumn(&model.Task{}, "Error")
return
}

func (r Migration) taskReportError(db *gorm.DB) (err error) {
type TaskReport struct {
model.TaskReport
Error string
}
var list []TaskReport
err = db.Find(&TaskReport{}, &list).Error
if err != nil {
return
}
for i := range list {
m := &list[i]
if m.Error == "" {
continue
}
m.Errors, _ = json.Marshal(
[]model.TaskError{
{
Severity: "Error",
Description: m.Error,
},
})
}
m := db.Migrator()
err = m.DropColumn(&model.TaskReport{}, "Error")
return
}
4 changes: 0 additions & 4 deletions migration/v6/model/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,8 @@ type Stakeholder = model.Stakeholder
type StakeholderGroup = model.StakeholderGroup
type Tag = model.Tag
type TagCategory = model.TagCategory
type Task = model.Task
type TaskGroup = model.TaskGroup
type TaskReport = model.TaskReport
type Ticket = model.Ticket
type Tracker = model.Tracker
type TTL = model.TTL
type ApplicationTag = model.ApplicationTag
type DependencyCyclicError = model.DependencyCyclicError

Expand Down
92 changes: 92 additions & 0 deletions migration/v6/model/task.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package model

import (
"encoding/json"
"fmt"
"gorm.io/gorm"
"time"
)

type Task struct {
Model
BucketOwner
Name string `gorm:"index"`
Addon string `gorm:"index"`
Locator string `gorm:"index"`
Priority int
Image string
Variant string
Policy string
TTL JSON
Data JSON
Started *time.Time
Terminated *time.Time
State string `gorm:"index"`
Errors JSON
Pod string `gorm:"index"`
Retries int
Canceled bool
Report *TaskReport `gorm:"constraint:OnDelete:CASCADE"`
ApplicationID *uint
Application *Application
TaskGroupID *uint `gorm:"<-:create"`
TaskGroup *TaskGroup
}

func (m *Task) Reset() {
m.Started = nil
m.Terminated = nil
m.Report = nil
m.Errors = nil
}

func (m *Task) BeforeCreate(db *gorm.DB) (err error) {
err = m.BucketOwner.BeforeCreate(db)
m.Reset()
return
}

//
// Error appends an error.
func (m *Task) Error(severity, description string, x ...interface{}) {
var list []TaskError
description = fmt.Sprintf(description, x...)
te := TaskError{Severity: severity, Description: description}
_ = json.Unmarshal(m.Errors, &list)
list = append(list, te)
m.Errors, _ = json.Marshal(list)
}

//
// Map alias.
type Map = map[string]interface{}

//
// TTL time-to-live.
type TTL struct {
Created int `json:"created,omitempty"`
Pending int `json:"pending,omitempty"`
Postponed int `json:"postponed,omitempty"`
Running int `json:"running,omitempty"`
Succeeded int `json:"succeeded,omitempty"`
Failed int `json:"failed,omitempty"`
}

//
// TaskError used in Task.Errors.
type TaskError struct {
Severity string `json:"severity"`
Description string `json:"description"`
}

type TaskReport struct {
Model
Status string
Errors JSON
Total int
Completed int
Activity JSON `gorm:"type:json"`
Result JSON `gorm:"type:json"`
TaskID uint `gorm:"<-:create;uniqueIndex"`
Task *Task
}
Loading