github.com/bigcommerce/nomad@v0.9.3-bc/command/operator_keyring.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/nomad/api"
     8  	"github.com/mitchellh/cli"
     9  	"github.com/posener/complete"
    10  )
    11  
    12  // OperatorKeyringCommand is a Command implementation that handles querying, installing,
    13  // and removing gossip encryption keys from a keyring.
    14  type OperatorKeyringCommand struct {
    15  	Meta
    16  }
    17  
    18  func (c *OperatorKeyringCommand) Help() string {
    19  	helpText := `
    20  Usage: nomad operator keyring [options]
    21  
    22    Manages encryption keys used for gossip messages between Nomad servers. Gossip
    23    encryption is optional. When enabled, this command may be used to examine
    24    active encryption keys in the cluster, add new keys, and remove old ones. When
    25    combined, this functionality provides the ability to perform key rotation
    26    cluster-wide, without disrupting the cluster.
    27  
    28    All operations performed by this command can only be run against server nodes.
    29  
    30    All variations of the keyring command return 0 if all nodes reply and there
    31    are no errors. If any node fails to reply or reports failure, the exit code
    32    will be 1.
    33  
    34  General Options:
    35  
    36    ` + generalOptionsUsage() + `
    37  
    38  Keyring Options:
    39  
    40    -install=<key>            Install a new encryption key. This will broadcast
    41                              the new key to all members in the cluster.
    42    -list                     List all keys currently in use within the cluster.
    43    -remove=<key>             Remove the given key from the cluster. This
    44                              operation may only be performed on keys which are
    45                              not currently the primary key.
    46    -use=<key>                Change the primary encryption key, which is used to
    47                              encrypt messages. The key must already be installed
    48                              before this operation can succeed.
    49  `
    50  	return strings.TrimSpace(helpText)
    51  }
    52  
    53  func (c *OperatorKeyringCommand) Synopsis() string {
    54  	return "Manages gossip layer encryption keys"
    55  }
    56  
    57  func (c *OperatorKeyringCommand) AutocompleteFlags() complete.Flags {
    58  	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
    59  		complete.Flags{
    60  			"-install": complete.PredictAnything,
    61  			"-list":    complete.PredictNothing,
    62  			"-remove":  complete.PredictAnything,
    63  			"-use":     complete.PredictAnything,
    64  		})
    65  }
    66  func (c *OperatorKeyringCommand) AutocompleteArgs() complete.Predictor {
    67  	return complete.PredictNothing
    68  }
    69  
    70  func (c *OperatorKeyringCommand) Name() string { return "operator keyring" }
    71  
    72  func (c *OperatorKeyringCommand) Run(args []string) int {
    73  	var installKey, useKey, removeKey string
    74  	var listKeys bool
    75  
    76  	flags := c.Meta.FlagSet("operator-keyring", FlagSetClient)
    77  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    78  
    79  	flags.StringVar(&installKey, "install", "", "install key")
    80  	flags.StringVar(&useKey, "use", "", "use key")
    81  	flags.StringVar(&removeKey, "remove", "", "remove key")
    82  	flags.BoolVar(&listKeys, "list", false, "list keys")
    83  
    84  	if err := flags.Parse(args); err != nil {
    85  		return 1
    86  	}
    87  
    88  	c.Ui = &cli.PrefixedUi{
    89  		OutputPrefix: "",
    90  		InfoPrefix:   "==> ",
    91  		ErrorPrefix:  "",
    92  		Ui:           c.Ui,
    93  	}
    94  
    95  	// Only accept a single argument
    96  	found := listKeys
    97  	for _, arg := range []string{installKey, useKey, removeKey} {
    98  		if found && len(arg) > 0 {
    99  			c.Ui.Error("Only a single action is allowed")
   100  			c.Ui.Error(commandErrorText(c))
   101  			return 1
   102  		}
   103  		found = found || len(arg) > 0
   104  	}
   105  
   106  	// Fail fast if no actionable args were passed
   107  	if !found {
   108  		c.Ui.Error("No actionable argument was passed")
   109  		c.Ui.Error("Either the '-install', '-use', '-remove' or '-list' flag must be set")
   110  		c.Ui.Error(commandErrorText(c))
   111  		return 1
   112  	}
   113  
   114  	// All other operations will require a client connection
   115  	client, err := c.Meta.Client()
   116  	if err != nil {
   117  		c.Ui.Error(fmt.Sprintf("Error creating nomad cli client: %s", err))
   118  		return 1
   119  	}
   120  
   121  	if listKeys {
   122  		c.Ui.Output("Gathering installed encryption keys...")
   123  		r, err := client.Agent().ListKeys()
   124  		if err != nil {
   125  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   126  			return 1
   127  		}
   128  		c.handleKeyResponse(r)
   129  		return 0
   130  	}
   131  
   132  	if installKey != "" {
   133  		c.Ui.Output("Installing new gossip encryption key...")
   134  		_, err := client.Agent().InstallKey(installKey)
   135  		if err != nil {
   136  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   137  			return 1
   138  		}
   139  		return 0
   140  	}
   141  
   142  	if useKey != "" {
   143  		c.Ui.Output("Changing primary gossip encryption key...")
   144  		_, err := client.Agent().UseKey(useKey)
   145  		if err != nil {
   146  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   147  			return 1
   148  		}
   149  		return 0
   150  	}
   151  
   152  	if removeKey != "" {
   153  		c.Ui.Output("Removing gossip encryption key...")
   154  		_, err := client.Agent().RemoveKey(removeKey)
   155  		if err != nil {
   156  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   157  			return 1
   158  		}
   159  		return 0
   160  	}
   161  
   162  	// Should never make it here
   163  	return 0
   164  }
   165  
   166  func (c *OperatorKeyringCommand) handleKeyResponse(resp *api.KeyringResponse) {
   167  	out := make([]string, len(resp.Keys)+1)
   168  	out[0] = "Key"
   169  	i := 1
   170  	for k := range resp.Keys {
   171  		out[i] = fmt.Sprintf("%s", k)
   172  		i = i + 1
   173  	}
   174  	c.Ui.Output(formatList(out))
   175  }