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

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/nomad/api/contexts"
     8  	"github.com/posener/complete"
     9  )
    10  
    11  type NodeEligibilityCommand struct {
    12  	Meta
    13  }
    14  
    15  func (c *NodeEligibilityCommand) Help() string {
    16  	helpText := `
    17  Usage: nomad node eligibility [options] <node>
    18  
    19    Toggles the nodes scheduling eligibility. When a node is marked as ineligible,
    20    no new allocations will be placed on it but existing allocations will remain.
    21    To remove existing allocations, use the node drain command.
    22  
    23    It is required that either -enable or -disable is specified, but not both.
    24    The -self flag is useful to set the scheduling eligibility of the local node.
    25  
    26    If ACLs are enabled, this option requires a token with the 'node:write'
    27    capability.
    28  
    29  General Options:
    30  
    31    ` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + `
    32  
    33  Node Eligibility Options:
    34  
    35    -disable
    36      Mark the specified node as ineligible for new allocations.
    37  
    38    -enable
    39      Mark the specified node as eligible for new allocations.
    40  
    41    -self
    42      Set the eligibility of the local node.
    43  `
    44  	return strings.TrimSpace(helpText)
    45  }
    46  
    47  func (c *NodeEligibilityCommand) Synopsis() string {
    48  	return "Toggle scheduling eligibility for a given node"
    49  }
    50  
    51  func (c *NodeEligibilityCommand) AutocompleteFlags() complete.Flags {
    52  	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
    53  		complete.Flags{
    54  			"-disable": complete.PredictNothing,
    55  			"-enable":  complete.PredictNothing,
    56  			"-self":    complete.PredictNothing,
    57  		})
    58  }
    59  
    60  func (c *NodeEligibilityCommand) AutocompleteArgs() complete.Predictor {
    61  	return complete.PredictFunc(func(a complete.Args) []string {
    62  		client, err := c.Meta.Client()
    63  		if err != nil {
    64  			return nil
    65  		}
    66  
    67  		resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Nodes, nil)
    68  		if err != nil {
    69  			return []string{}
    70  		}
    71  		return resp.Matches[contexts.Nodes]
    72  	})
    73  }
    74  
    75  func (c *NodeEligibilityCommand) Name() string { return "node eligibility" }
    76  
    77  func (c *NodeEligibilityCommand) Run(args []string) int {
    78  	var enable, disable, self bool
    79  
    80  	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
    81  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    82  	flags.BoolVar(&enable, "enable", false, "Mark node as eligibile for scheduling")
    83  	flags.BoolVar(&disable, "disable", false, "Mark node as ineligibile for scheduling")
    84  	flags.BoolVar(&self, "self", false, "")
    85  
    86  	if err := flags.Parse(args); err != nil {
    87  		return 1
    88  	}
    89  
    90  	// Check that we got either enable or disable, but not both.
    91  	if (enable && disable) || (!enable && !disable) {
    92  		c.Ui.Error("Either the '-enable' or '-disable' flag must be set")
    93  		c.Ui.Error(commandErrorText(c))
    94  		return 1
    95  	}
    96  
    97  	// Check that we got a node ID
    98  	args = flags.Args()
    99  	if l := len(args); self && l != 0 || !self && l != 1 {
   100  		c.Ui.Error("Node ID must be specified if -self isn't being used")
   101  		c.Ui.Error(commandErrorText(c))
   102  		return 1
   103  	}
   104  
   105  	// Get the HTTP client
   106  	client, err := c.Meta.Client()
   107  	if err != nil {
   108  		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
   109  		return 1
   110  	}
   111  
   112  	// If -self flag is set then determine the current node.
   113  	var nodeID string
   114  	if !self {
   115  		nodeID = args[0]
   116  	} else {
   117  		var err error
   118  		if nodeID, err = getLocalNodeID(client); err != nil {
   119  			c.Ui.Error(err.Error())
   120  			return 1
   121  		}
   122  	}
   123  
   124  	// Check if node exists
   125  	if len(nodeID) == 1 {
   126  		c.Ui.Error("Identifier must contain at least two characters.")
   127  		return 1
   128  	}
   129  
   130  	nodeID = sanitizeUUIDPrefix(nodeID)
   131  	nodes, _, err := client.Nodes().PrefixList(nodeID)
   132  	if err != nil {
   133  		c.Ui.Error(fmt.Sprintf("Error updating scheduling eligibility: %s", err))
   134  		return 1
   135  	}
   136  	// Return error if no nodes are found
   137  	if len(nodes) == 0 {
   138  		c.Ui.Error(fmt.Sprintf("No node(s) with prefix or id %q found", nodeID))
   139  		return 1
   140  	}
   141  	if len(nodes) > 1 {
   142  		c.Ui.Error(fmt.Sprintf("Prefix matched multiple nodes\n\n%s",
   143  			formatNodeStubList(nodes, true)))
   144  		return 1
   145  	}
   146  
   147  	// Prefix lookup matched a single node
   148  	node, _, err := client.Nodes().Info(nodes[0].ID, nil)
   149  	if err != nil {
   150  		c.Ui.Error(fmt.Sprintf("Error updating scheduling eligibility: %s", err))
   151  		return 1
   152  	}
   153  
   154  	// Toggle node eligibility
   155  	if _, err := client.Nodes().ToggleEligibility(node.ID, enable, nil); err != nil {
   156  		c.Ui.Error(fmt.Sprintf("Error updating scheduling eligibility: %s", err))
   157  		return 1
   158  	}
   159  
   160  	if enable {
   161  		c.Ui.Output(fmt.Sprintf("Node %q scheduling eligibility set: eligible for scheduling", node.ID))
   162  	} else {
   163  		c.Ui.Output(fmt.Sprintf("Node %q scheduling eligibility set: ineligible for scheduling", node.ID))
   164  	}
   165  	return 0
   166  }