// Copyright (C) 2020 Marius Schellenberger package pve import ( "context" "errors" "fmt" "strings" "time" ) const ( TaskTimeout = 300 TaskStatusCheckInterval = 2 TaskExitStatusOK = "OK" TaskStatusRunning = "running" TaskStatusStopped = "stopped" ) type Task struct { ID string `json:"upid"` Type string `json:"type"` Status string `json:"status"` Exitstatus string `json:"exitstatus"` } type TaskClient struct { client *Client } func nodeFromTaskID(taskid string) (string, error) { a := strings.SplitN(taskid, ":", 3) if len(a) < 2 { return "", errors.New("error parsing taskid") } return a[1], nil } func (c *TaskClient) MustGet(ctx context.Context, taskid string) (t *Task) { t = &Task{ID: taskid} task, err := c.Get(ctx, taskid) if err == nil { t = task } return } func (c *TaskClient) Get(ctx context.Context, taskid string) (t *Task, err error) { node, err := nodeFromTaskID(taskid) if err != nil { return } req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/nodes/%s/tasks/%s/status", node, taskid), nil) if err != nil { return } t = &Task{ID: taskid} _, err = c.client.Do(req, t) return } type OnTaskChange func(t *Task) func (c *TaskClient) Wait(ctx context.Context, t *Task, f OnTaskChange) error { if f != nil && t.Status != "" { f(t) } if t.Status == TaskStatusStopped { if t.Exitstatus == TaskExitStatusOK { return nil } return errors.New(t.Exitstatus) } waited := 0 for waited < TaskTimeout { task, err := c.Get(ctx, t.ID) if err != nil { return err } if task.Status != t.Status { if f != nil { f(task) } if task.Status == TaskStatusStopped { if task.Exitstatus == TaskExitStatusOK { return nil } return errors.New(task.Exitstatus) } } time.Sleep(TaskStatusCheckInterval * time.Second) waited = waited + TaskStatusCheckInterval } return errors.New("task wait timeout for: " + t.ID) }