package models import ( "encoding/json" "fmt" "strconv" "strings" "time" "github.com/acarl005/stripansi" "github.com/rs/zerolog" ) // An object to monitor the logs generated by a specific pod from a workflow execution type ArgoWatch struct { Name string Namespace string Status string Conditions Created string Started string Duration string Progress string Logs []string } type Conditions struct { PodRunning bool Completed bool } func (a *ArgoWatch) Equals(arg *ArgoWatch) bool { if arg == nil { return false } return a.Status == arg.Status && a.Progress == arg.Progress && a.Conditions.PodRunning == arg.Conditions.PodRunning && a.Conditions.Completed == arg.Conditions.Completed } func NewArgoLogs(name string, namespace string, stepMax int) *ArgoLogs { return &ArgoLogs{ Name: "oc-monitor-" + name, Namespace: namespace, CreatedDate: time.Now().Format("2006-01-02 15:04:05"), StepCount: 0, StepMax: stepMax, stop: false, Seen: []string{}, } } // An object to monitor and log the output of an argo submit type ArgoLogs struct { Name string Namespace string CreatedDate string StepCount int StepMax int stop bool Started time.Time Seen []string Logs []string IsStreaming bool } func (a *ArgoLogs) NewWatch() *ArgoWatch { return &ArgoWatch{ Name: a.Name, Namespace: a.Namespace, Status: "Pending", Created: a.CreatedDate, Started: a.Started.Format("2006-01-02 15:04:05"), Conditions: Conditions{ PodRunning: a.StepCount > 0 && a.StepCount < a.StepMax, Completed: a.StepCount == a.StepMax, }, Progress: fmt.Sprintf("%v/%v", a.StepCount, a.StepMax), Duration: "0s", Logs: []string{}, } } func (a *ArgoLogs) StartStepRecording(current_watch *ArgoWatch, logger zerolog.Logger) { jsonified, _ := json.Marshal(current_watch) logger.Info().Msg(string(jsonified)) a.StepCount += 1 a.Started = time.Now() } func (a *ArgoLogs) StopStepRecording(current *ArgoWatch) *ArgoWatch { fn := strings.Split(a.Name, "_") logs := []string{} err := false end := "" for _, input := range current.Logs { line := strings.TrimSpace(input) if line == "" || !strings.Contains(line, fn[0]) || !strings.Contains(line, ":") { continue } step := strings.Split(line, ":") if strings.Contains(line, "sub-process exited") { b := strings.Split(line, "time=\"") if len(b) > 1 { end = b[1][:19] } } if len(step) < 2 || strings.Contains(line, "time=") || strings.TrimSpace(strings.Join(step[1:], " : ")) == "" || strings.TrimSpace(strings.Join(step[1:], " : ")) == a.Name { continue } log := stripansi.Strip(strings.TrimSpace(strings.Join(step[1:], " : "))) t, e := strconv.Unquote(log) if e == nil { logs = append(logs, t) } else { logs = append(logs, strings.ReplaceAll(log, "\"", "`")) } if strings.Contains(logs[len(logs)-1], "Error") { err = true } } status := "Pending" if a.StepCount > 0 { status = "Running" } if a.StepCount == a.StepMax { if err { status = "Failed" } else { status = "Succeeded" } } duration := float64(0) if end != "" { timeE, _ := time.Parse("2006-01-02T15:04:05", end) duration = timeE.Sub(a.Started).Seconds() } current.Conditions = Conditions{ PodRunning: a.StepCount > 0 && a.StepCount < a.StepMax, Completed: a.StepCount == a.StepMax, } current.Progress = fmt.Sprintf("%v/%v", a.StepCount, a.StepMax) current.Duration = fmt.Sprintf("%v", fmt.Sprintf("%.2f", duration)+"s") current.Status = status return current }