// StatusHistory returns a slice of past statuses for several entities. func (c *Client) StatusHistory(request params.StatusHistoryRequests) params.StatusHistoryResults { results := params.StatusHistoryResults{} // TODO(perrito666) the contents of the loop could be split into // a oneHistory method for clarity. for _, request := range request.Requests { filter := status.StatusHistoryFilter{ Size: request.Filter.Size, Date: request.Filter.Date, Delta: request.Filter.Delta, } if err := c.checkCanRead(); err != nil { history := params.StatusHistoryResult{ Error: common.ServerError(err), } results.Results = append(results.Results, history) continue } if err := filter.Validate(); err != nil { history := params.StatusHistoryResult{ Error: common.ServerError(errors.Annotate(err, "cannot validate status history filter")), } results.Results = append(results.Results, history) continue } var ( err error hist []params.DetailedStatus ) kind := status.HistoryKind(request.Kind) err = errors.NotValidf("%q requires a unit, got %T", kind, request.Tag) switch kind { case status.KindUnit, status.KindWorkload, status.KindUnitAgent: var u names.UnitTag if u, err = names.ParseUnitTag(request.Tag); err == nil { hist, err = c.unitStatusHistory(u, filter, kind) } default: var m names.MachineTag if m, err = names.ParseMachineTag(request.Tag); err == nil { hist, err = c.machineStatusHistory(m, filter, kind) } } if err == nil { sort.Sort(byTime(hist)) } results.Results = append(results.Results, params.StatusHistoryResult{ History: params.History{Statuses: hist}, Error: common.ServerError(errors.Annotatef(err, "fetching status history for %q", request.Tag)), }) } return results }
// StatusHistory retrieves the last <size> results of // <kind:combined|agent|workload|machine|machineinstance|container|containerinstance> status // for <name> unit func (c *Client) StatusHistory(kind status.HistoryKind, tag names.Tag, filter status.StatusHistoryFilter) (status.History, error) { var results params.StatusHistoryResults args := params.StatusHistoryRequest{ Kind: string(kind), Filter: params.StatusHistoryFilter{ Size: filter.Size, Date: filter.Date, Delta: filter.Delta, }, Tag: tag.String(), } bulkArgs := params.StatusHistoryRequests{Requests: []params.StatusHistoryRequest{args}} err := c.facade.FacadeCall("StatusHistory", bulkArgs, &results) if err != nil { return status.History{}, errors.Trace(err) } if len(results.Results) != 1 { return status.History{}, errors.Errorf("expected 1 result got %d", len(results.Results)) } if results.Results[0].Error != nil { return status.History{}, errors.Annotatef(results.Results[0].Error, "while processing the request") } history := make(status.History, len(results.Results[0].History.Statuses)) if results.Results[0].History.Error != nil { return status.History{}, results.Results[0].History.Error } for i, h := range results.Results[0].History.Statuses { history[i] = status.DetailedStatus{ Status: status.Status(h.Status), Info: h.Info, Data: h.Data, Since: h.Since, Kind: status.HistoryKind(h.Kind), Version: h.Version, // TODO(perrito666) make sure these are still used. Life: h.Life, Err: h.Err, } // TODO(perrito666) https://launchpad.net/bugs/1577589 if !history[i].Kind.Valid() { logger.Errorf("history returned an unknown status kind %q", h.Kind) } } return history, nil }
func (c *statusHistoryCommand) Init(args []string) error { switch { case len(args) > 1: return errors.Errorf("unexpected arguments after entity name.") case len(args) == 0: return errors.Errorf("entity name is missing.") default: c.entityName = args[0] } // If use of ISO time not specified on command line, // check env var. if !c.isoTime { var err error envVarValue := os.Getenv(osenv.JujuStatusIsoTimeEnvKey) if envVarValue != "" { if c.isoTime, err = strconv.ParseBool(envVarValue); err != nil { return errors.Annotatef(err, "invalid %s env var, expected true|false", osenv.JujuStatusIsoTimeEnvKey) } } } emptyDate := c.backlogDate == "" emptySize := c.backlogSize == 0 emptyDays := c.backlogSizeDays == 0 if emptyDate && emptySize && emptyDays { c.backlogSize = 20 } if (!emptyDays && !emptySize) || (!emptyDays && !emptyDate) || (!emptySize && !emptyDate) { return errors.Errorf("backlog size, backlog date and backlog days back cannot be specified together") } if c.backlogDate != "" { var err error c.date, err = time.Parse("2006-01-02", c.backlogDate) if err != nil { return errors.Annotate(err, "parsing backlog date") } } kind := status.HistoryKind(c.outputContent) if kind.Valid() { return nil } return errors.Errorf("unexpected status type %q", c.outputContent) }
func (c *statusHistoryCommand) Run(ctx *cmd.Context) error { apiclient, err := c.NewAPIClient() if err != nil { return errors.Trace(err) } defer apiclient.Close() kind := status.HistoryKind(c.outputContent) var delta *time.Duration if c.backlogSizeDays != 0 { t := time.Duration(c.backlogSizeDays*24) * time.Hour delta = &t } filterArgs := status.StatusHistoryFilter{ Size: c.backlogSize, Delta: delta, } if !c.date.IsZero() { filterArgs.Date = &c.date } var tag names.Tag switch kind { case status.KindUnit, status.KindWorkload, status.KindUnitAgent: if !names.IsValidUnit(c.entityName) { return errors.Errorf("%q is not a valid name for a %s", c.entityName, kind) } tag = names.NewUnitTag(c.entityName) default: if !names.IsValidMachine(c.entityName) { return errors.Errorf("%q is not a valid name for a %s", c.entityName, kind) } tag = names.NewMachineTag(c.entityName) } statuses, err := apiclient.StatusHistory(kind, tag, filterArgs) historyLen := len(statuses) if err != nil { if historyLen == 0 { return errors.Trace(err) } // Display any error, but continue to print status if some was returned fmt.Fprintf(ctx.Stderr, "%v\n", err) } if historyLen == 0 { return errors.Errorf("no status history available") } table := [][]string{{"TIME", "TYPE", "STATUS", "MESSAGE"}} lengths := []int{1, 1, 1, 1} statuses = statuses.SquashLogs(1) statuses = statuses.SquashLogs(2) statuses = statuses.SquashLogs(3) for _, v := range statuses { fields := []string{common.FormatTime(v.Since, c.isoTime), string(v.Kind), string(v.Status), v.Info} for k, v := range fields { if len(v) > lengths[k] { lengths[k] = len(v) } } table = append(table, fields) } f := fmt.Sprintf("%%-%ds\t%%-%ds\t%%-%ds\t%%-%ds\n", lengths[0], lengths[1], lengths[2], lengths[3]) for _, v := range table { fmt.Printf(f, v[0], v[1], v[2], v[3]) } return nil }