github.com/outbrain/consul@v1.4.5/command/maint/maint.go (about)

     1  package maint
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/consul/command/flags"
     9  	"github.com/mitchellh/cli"
    10  )
    11  
    12  // cmd is a Command implementation that enables or disables
    13  // node or service maintenance mode.
    14  type cmd struct {
    15  	UI    cli.Ui
    16  	help  string
    17  	flags *flag.FlagSet
    18  	http  *flags.HTTPFlags
    19  
    20  	// flags
    21  	enable    bool
    22  	disable   bool
    23  	reason    string
    24  	serviceID string
    25  }
    26  
    27  func New(ui cli.Ui) *cmd {
    28  	c := &cmd{UI: ui}
    29  	c.init()
    30  	return c
    31  }
    32  
    33  func (c *cmd) init() {
    34  	c.flags = flag.NewFlagSet("", flag.ContinueOnError)
    35  	c.flags.BoolVar(&c.enable, "enable", false,
    36  		"Enable maintenance mode.")
    37  	c.flags.BoolVar(&c.disable, "disable", false,
    38  		"Disable maintenance mode.")
    39  	c.flags.StringVar(&c.reason, "reason", "",
    40  		"Text describing the maintenance reason.")
    41  	c.flags.StringVar(&c.serviceID, "service", "",
    42  		"Control maintenance mode for a specific service ID.")
    43  
    44  	c.http = &flags.HTTPFlags{}
    45  	flags.Merge(c.flags, c.http.ClientFlags())
    46  	c.help = flags.Usage(help, c.flags)
    47  }
    48  
    49  func (c *cmd) Run(args []string) int {
    50  	if err := c.flags.Parse(args); err != nil {
    51  		return 1
    52  	}
    53  
    54  	// Ensure we don't have conflicting args
    55  	if c.enable && c.disable {
    56  		c.UI.Error("Only one of -enable or -disable may be provided")
    57  		return 1
    58  	}
    59  	if !c.enable && c.reason != "" {
    60  		c.UI.Error("Reason may only be provided with -enable")
    61  		return 1
    62  	}
    63  	if !c.enable && !c.disable && c.serviceID != "" {
    64  		c.UI.Error("Service requires either -enable or -disable")
    65  		return 1
    66  	}
    67  
    68  	// Create and test the HTTP client
    69  	client, err := c.http.APIClient()
    70  	if err != nil {
    71  		c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
    72  		return 1
    73  	}
    74  	a := client.Agent()
    75  	nodeName, err := a.NodeName()
    76  	if err != nil {
    77  		c.UI.Error(fmt.Sprintf("Error querying Consul agent: %s", err))
    78  		return 1
    79  	}
    80  
    81  	if !c.enable && !c.disable {
    82  		// List mode - list nodes/services in maintenance mode
    83  		checks, err := a.Checks()
    84  		if err != nil {
    85  			c.UI.Error(fmt.Sprintf("Error getting checks: %s", err))
    86  			return 1
    87  		}
    88  
    89  		for _, check := range checks {
    90  			if check.CheckID == "_node_maintenance" {
    91  				c.UI.Output("Node:")
    92  				c.UI.Output("  Name:   " + nodeName)
    93  				c.UI.Output("  Reason: " + check.Notes)
    94  				c.UI.Output("")
    95  			} else if strings.HasPrefix(string(check.CheckID), "_service_maintenance:") {
    96  				c.UI.Output("Service:")
    97  				c.UI.Output("  ID:     " + check.ServiceID)
    98  				c.UI.Output("  Reason: " + check.Notes)
    99  				c.UI.Output("")
   100  			}
   101  		}
   102  
   103  		return 0
   104  	}
   105  
   106  	if c.enable {
   107  		// Enable node maintenance
   108  		if c.serviceID == "" {
   109  			if err := a.EnableNodeMaintenance(c.reason); err != nil {
   110  				c.UI.Error(fmt.Sprintf("Error enabling node maintenance: %s", err))
   111  				return 1
   112  			}
   113  			c.UI.Output("Node maintenance is now enabled")
   114  			return 0
   115  		}
   116  
   117  		// Enable service maintenance
   118  		if err := a.EnableServiceMaintenance(c.serviceID, c.reason); err != nil {
   119  			c.UI.Error(fmt.Sprintf("Error enabling service maintenance: %s", err))
   120  			return 1
   121  		}
   122  		c.UI.Output(fmt.Sprintf("Service maintenance is now enabled for %q", c.serviceID))
   123  		return 0
   124  	}
   125  
   126  	if c.disable {
   127  		// Disable node maintenance
   128  		if c.serviceID == "" {
   129  			if err := a.DisableNodeMaintenance(); err != nil {
   130  				c.UI.Error(fmt.Sprintf("Error disabling node maintenance: %s", err))
   131  				return 1
   132  			}
   133  			c.UI.Output("Node maintenance is now disabled")
   134  			return 0
   135  		}
   136  
   137  		// Disable service maintenance
   138  		if err := a.DisableServiceMaintenance(c.serviceID); err != nil {
   139  			c.UI.Error(fmt.Sprintf("Error disabling service maintenance: %s", err))
   140  			return 1
   141  		}
   142  		c.UI.Output(fmt.Sprintf("Service maintenance is now disabled for %q", c.serviceID))
   143  		return 0
   144  	}
   145  
   146  	return 0
   147  }
   148  
   149  func (c *cmd) Synopsis() string {
   150  	return synopsis
   151  }
   152  
   153  func (c *cmd) Help() string {
   154  	return c.help
   155  }
   156  
   157  const synopsis = "Controls node or service maintenance mode"
   158  const help = `
   159  Usage: consul maint [options]
   160  
   161    Places a node or service into maintenance mode. During maintenance mode,
   162    the node or service will be excluded from all queries through the DNS
   163    or API interfaces, effectively taking it out of the pool of available
   164    nodes. This is done by registering an additional critical health check.
   165  
   166    When enabling maintenance mode for a node or service, you may optionally
   167    specify a reason string. This string will appear in the "Notes" field
   168    of the critical health check which is registered against the node or
   169    service. If no reason is provided, a default value will be used.
   170  
   171    Maintenance mode is persistent, and will be restored in the event of an
   172    agent restart. It is therefore required to disable maintenance mode on
   173    a given node or service before it will be placed back into the pool.
   174  
   175    By default, we operate on the node as a whole. By specifying the
   176    "-service" argument, this behavior can be changed to enable or disable
   177    only a specific service.
   178  
   179    If no arguments are given, the agent's maintenance status will be shown.
   180    This will return blank if nothing is currently under maintenance.
   181  `