github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/alloc_restart.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/nomad/api"
     8  	"github.com/hashicorp/nomad/api/contexts"
     9  	"github.com/posener/complete"
    10  )
    11  
    12  type AllocRestartCommand struct {
    13  	Meta
    14  }
    15  
    16  func (c *AllocRestartCommand) Help() string {
    17  	helpText := `
    18  Usage: nomad alloc restart [options] <allocation> <task>
    19  
    20    Restart an existing allocation. This command is used to restart a specific alloc
    21    and its tasks. If no task is provided then all of the allocation's tasks that
    22    are currently running will be restarted.
    23  
    24    Use the option '-all-tasks' to restart tasks that have already run, such as
    25    non-sidecar prestart and poststart tasks.
    26  
    27    When ACLs are enabled, this command requires a token with the
    28    'alloc-lifecycle', 'read-job', and 'list-jobs' capabilities for the
    29    allocation's namespace.
    30  
    31  General Options:
    32  
    33    ` + generalOptionsUsage(usageOptsDefault) + `
    34  
    35  Restart Specific Options:
    36  
    37    -all-tasks
    38      If set, all tasks in the allocation will be restarted, even the ones that
    39      already ran. This option cannot be used with '-task' or the '<task>'
    40      argument.
    41  
    42    -task <task-name>
    43      Specify the individual task to restart. If task name is given with both an
    44      argument and the '-task' option, preference is given to the '-task' option.
    45      This option cannot be used with '-all-tasks'.
    46  
    47    -verbose
    48      Show full information.
    49  `
    50  	return strings.TrimSpace(helpText)
    51  }
    52  
    53  func (c *AllocRestartCommand) Name() string { return "alloc restart" }
    54  
    55  func (c *AllocRestartCommand) Run(args []string) int {
    56  	var allTasks, verbose bool
    57  	var task string
    58  
    59  	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
    60  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    61  	flags.BoolVar(&allTasks, "all-tasks", false, "")
    62  	flags.BoolVar(&verbose, "verbose", false, "")
    63  	flags.StringVar(&task, "task", "", "")
    64  
    65  	if err := flags.Parse(args); err != nil {
    66  		return 1
    67  	}
    68  
    69  	// Check that we got exactly one alloc
    70  	args = flags.Args()
    71  	if len(args) < 1 || len(args) > 2 {
    72  		c.Ui.Error("This command takes one or two arguments: <alloc-id> <task-name>")
    73  		c.Ui.Error(commandErrorText(c))
    74  		return 1
    75  	}
    76  
    77  	allocID := args[0]
    78  
    79  	// If -task isn't provided fallback to reading the task name
    80  	// from args.
    81  	if task == "" && len(args) >= 2 {
    82  		task = args[1]
    83  	}
    84  
    85  	if allTasks && task != "" {
    86  		c.Ui.Error("The -all-tasks option is not allowed when restarting a specific task.")
    87  		return 1
    88  	}
    89  
    90  	// Truncate the id unless full length is requested
    91  	length := shortId
    92  	if verbose {
    93  		length = fullId
    94  	}
    95  
    96  	// Query the allocation info
    97  	if len(allocID) == 1 {
    98  		c.Ui.Error("Alloc ID must contain at least two characters.")
    99  		return 1
   100  	}
   101  
   102  	allocID = sanitizeUUIDPrefix(allocID)
   103  
   104  	// Get the HTTP client
   105  	client, err := c.Meta.Client()
   106  	if err != nil {
   107  		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
   108  		return 1
   109  	}
   110  
   111  	allocs, _, err := client.Allocations().PrefixList(allocID)
   112  	if err != nil {
   113  		c.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err))
   114  		return 1
   115  	}
   116  
   117  	if len(allocs) == 0 {
   118  		c.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID))
   119  		return 1
   120  	}
   121  
   122  	if len(allocs) > 1 {
   123  		// Format the allocs
   124  		out := formatAllocListStubs(allocs, verbose, length)
   125  		c.Ui.Error(fmt.Sprintf("Prefix matched multiple allocations\n\n%s", out))
   126  		return 1
   127  	}
   128  
   129  	// Prefix lookup matched a single allocation
   130  	q := &api.QueryOptions{Namespace: allocs[0].Namespace}
   131  	alloc, _, err := client.Allocations().Info(allocs[0].ID, q)
   132  	if err != nil {
   133  		c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
   134  		return 1
   135  	}
   136  
   137  	if task != "" {
   138  		err := validateTaskExistsInAllocation(task, alloc)
   139  		if err != nil {
   140  			c.Ui.Error(err.Error())
   141  			return 1
   142  		}
   143  	}
   144  
   145  	if allTasks {
   146  		err = client.Allocations().RestartAllTasks(alloc, nil)
   147  	} else {
   148  		err = client.Allocations().Restart(alloc, task, nil)
   149  	}
   150  	if err != nil {
   151  		target := "allocation"
   152  		if task != "" {
   153  			target = "task"
   154  		}
   155  		c.Ui.Error(fmt.Sprintf("Failed to restart %s:\n\n%s", target, err.Error()))
   156  		return 1
   157  	}
   158  
   159  	return 0
   160  }
   161  
   162  func validateTaskExistsInAllocation(taskName string, alloc *api.Allocation) error {
   163  	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
   164  	if tg == nil {
   165  		return fmt.Errorf("Could not find allocation task group: %s", alloc.TaskGroup)
   166  	}
   167  
   168  	foundTaskNames := make([]string, len(tg.Tasks))
   169  	for i, task := range tg.Tasks {
   170  		foundTaskNames[i] = task.Name
   171  		if task.Name == taskName {
   172  			return nil
   173  		}
   174  	}
   175  
   176  	return fmt.Errorf("Could not find task named: %s, found:\n%s", taskName, formatList(foundTaskNames))
   177  }
   178  
   179  func (c *AllocRestartCommand) Synopsis() string {
   180  	return "Restart a running allocation"
   181  }
   182  
   183  func (c *AllocRestartCommand) AutocompleteArgs() complete.Predictor {
   184  	// Here we attempt to autocomplete allocations for any position of arg.
   185  	// We should eventually try to auto complete the task name if the arg is
   186  	// at position 2.
   187  	return complete.PredictFunc(func(a complete.Args) []string {
   188  		client, err := c.Meta.Client()
   189  		if err != nil {
   190  			return nil
   191  		}
   192  
   193  		resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Allocs, nil)
   194  		if err != nil {
   195  			return []string{}
   196  		}
   197  		return resp.Matches[contexts.Allocs]
   198  	})
   199  }