github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/action/status.go (about)

     1  // Copyright 2014-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package action
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/gnuflag"
    12  	"gopkg.in/juju/names.v2"
    13  
    14  	"github.com/juju/juju/apiserver/params"
    15  	jujucmd "github.com/juju/juju/cmd"
    16  	"github.com/juju/juju/cmd/modelcmd"
    17  	"github.com/juju/juju/cmd/output"
    18  )
    19  
    20  func NewStatusCommand() cmd.Command {
    21  	return modelcmd.Wrap(&statusCommand{})
    22  }
    23  
    24  // statusCommand shows the status of an Action by ID.
    25  type statusCommand struct {
    26  	ActionCommandBase
    27  	out         cmd.Output
    28  	requestedId string
    29  	name        string
    30  }
    31  
    32  const statusDoc = `
    33  Show the status of Actions matching given ID, partial ID prefix, or all Actions if no ID is supplied.
    34  If --name <name> is provided the search will be done by name rather than by ID.
    35  `
    36  
    37  // Set up the output.
    38  func (c *statusCommand) SetFlags(f *gnuflag.FlagSet) {
    39  	c.ActionCommandBase.SetFlags(f)
    40  	c.out.AddFlags(f, "yaml", output.DefaultFormatters)
    41  	f.StringVar(&c.name, "name", "", "Action name")
    42  }
    43  
    44  func (c *statusCommand) Info() *cmd.Info {
    45  	return jujucmd.Info(&cmd.Info{
    46  		Name:    "show-action-status",
    47  		Args:    "[<action ID>|<action ID prefix>]",
    48  		Purpose: "Show results of all actions filtered by optional ID prefix.",
    49  		Doc:     statusDoc,
    50  	})
    51  }
    52  
    53  func (c *statusCommand) Init(args []string) error {
    54  	switch len(args) {
    55  	case 0:
    56  		c.requestedId = ""
    57  		return nil
    58  	case 1:
    59  		c.requestedId = args[0]
    60  		return nil
    61  	default:
    62  		return cmd.CheckEmpty(args[1:])
    63  	}
    64  }
    65  
    66  func (c *statusCommand) Run(ctx *cmd.Context) error {
    67  	api, err := c.NewActionAPIClient()
    68  	if err != nil {
    69  		return err
    70  	}
    71  	defer api.Close()
    72  
    73  	if c.name != "" {
    74  		actions, err := GetActionsByName(api, c.name)
    75  		if err != nil {
    76  			return errors.Trace(err)
    77  		}
    78  		return c.out.Write(ctx, resultsToMap(actions))
    79  	}
    80  
    81  	actionTags, err := getActionTagsByPrefix(api, c.requestedId)
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	if len(actionTags) < 1 {
    87  		if len(c.requestedId) == 0 {
    88  			return errors.Errorf("no actions found")
    89  		} else {
    90  			return errors.Errorf("no actions found matching prefix %q", c.requestedId)
    91  		}
    92  	}
    93  
    94  	entities := []params.Entity{}
    95  	for _, tag := range actionTags {
    96  		entities = append(entities, params.Entity{Tag: tag.String()})
    97  	}
    98  
    99  	actions, err := api.Actions(params.Entities{Entities: entities})
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	if len(actions.Results) < 1 {
   105  		return errors.Errorf("identifier %q matched action(s) %v, but found no results", c.requestedId, actionTags)
   106  	}
   107  
   108  	return c.out.Write(ctx, resultsToMap(actions.Results))
   109  }
   110  
   111  // resultsToMap is a helper function that takes in a []params.ActionResult
   112  // and returns a map[string]interface{} ready to be served to the
   113  // formatter for printing.
   114  func resultsToMap(results []params.ActionResult) map[string]interface{} {
   115  	items := []map[string]interface{}{}
   116  	for _, item := range results {
   117  		items = append(items, resultToMap(item))
   118  	}
   119  	return map[string]interface{}{"actions": items}
   120  }
   121  
   122  func resultToMap(result params.ActionResult) map[string]interface{} {
   123  	item := map[string]interface{}{}
   124  	if result.Error != nil {
   125  		item["error"] = result.Error.Error()
   126  	}
   127  	if result.Action != nil {
   128  		item["action"] = result.Action.Name
   129  		atag, err := names.ParseActionTag(result.Action.Tag)
   130  		if err != nil {
   131  			item["id"] = result.Action.Tag
   132  		} else {
   133  			item["id"] = atag.Id()
   134  		}
   135  
   136  		rtag, err := names.ParseUnitTag(result.Action.Receiver)
   137  		if err != nil {
   138  			item["unit"] = result.Action.Receiver
   139  		} else {
   140  			item["unit"] = rtag.Id()
   141  		}
   142  
   143  	}
   144  	item["status"] = result.Status
   145  
   146  	// result.Completed uses the zero-value to indicate not completed
   147  	if result.Completed.Equal(time.Time{}) {
   148  		item["completed at"] = "n/a"
   149  	} else {
   150  		item["completed at"] = result.Completed.UTC().Format("2006-01-02 15:04:05")
   151  	}
   152  
   153  	return item
   154  }
   155  
   156  // GetActionsByName takes an action APIClient and a name and returns a list of
   157  // ActionResults.
   158  func GetActionsByName(api APIClient, name string) ([]params.ActionResult, error) {
   159  	nothing := []params.ActionResult{}
   160  	results, err := api.FindActionsByNames(params.FindActionsByNames{ActionNames: []string{name}})
   161  	if err != nil {
   162  		return nothing, errors.Trace(err)
   163  	}
   164  	if len(results.Actions) != 1 {
   165  		return nothing, errors.Errorf("expected one result got %d", len(results.Actions))
   166  	}
   167  	result := results.Actions[0]
   168  	if result.Error != nil {
   169  		return nothing, result.Error
   170  	}
   171  	if len(result.Actions) < 1 {
   172  		return nothing, errors.Errorf("no actions were found for name %s", name)
   173  	}
   174  	return result.Actions, nil
   175  
   176  }