diff --git a/command/acl.go b/command/acl.go new file mode 100644 index 000000000..7073085d5 --- /dev/null +++ b/command/acl.go @@ -0,0 +1,19 @@ +package command + +import "github.com/mitchellh/cli" + +type ACLCommand struct { + Meta +} + +func (f *ACLCommand) Help() string { + return "This command is accessed by using one of the subcommands below." +} + +func (f *ACLCommand) Synopsis() string { + return "Interact with ACL policies and tokens" +} + +func (f *ACLCommand) Run(args []string) int { + return cli.RunResultHelp +} diff --git a/command/acl_bootstrap.go b/command/acl_bootstrap.go new file mode 100644 index 000000000..985ecc92d --- /dev/null +++ b/command/acl_bootstrap.go @@ -0,0 +1,100 @@ +package command + +import ( + "fmt" + "strings" + + "github.com/hashicorp/nomad/api" + "github.com/posener/complete" +) + +type ACLBootstrapCommand struct { + Meta +} + +func (c *ACLBootstrapCommand) Help() string { + helpText := ` +Usage: nomad acl bootstrap [options] + +Bootstrap is used to bootstrap the ACL system and get an initial token. + +General Options: + + ` + generalOptionsUsage() + ` + +` + return strings.TrimSpace(helpText) +} + +func (c *ACLBootstrapCommand) AutocompleteFlags() complete.Flags { + return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), + complete.Flags{}) +} + +func (c *ACLBootstrapCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +func (c *ACLBootstrapCommand) Synopsis() string { + return "Bootstrap the ACL system for initial token" +} + +func (c *ACLBootstrapCommand) Run(args []string) int { + flags := c.Meta.FlagSet("acl bootstrap", FlagSetClient) + flags.Usage = func() { c.Ui.Output(c.Help()) } + if err := flags.Parse(args); err != nil { + return 1 + } + + // Check that we got no arguments + args = flags.Args() + if l := len(args); l != 0 { + c.Ui.Error(c.Help()) + return 1 + } + + // Get the HTTP client + client, err := c.Meta.Client() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) + return 1 + } + + // Get the bootstrap token + token, _, err := client.ACLTokens().Bootstrap(nil) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error bootstrapping: %s", err)) + return 1 + } + + // Format the output + c.Ui.Output(formatKVACLToken(token)) + return 0 +} + +// formatKVACLToken returns a K/V formatted ACL token +func formatKVACLToken(token *api.ACLToken) string { + // Add the fixed preamble + output := []string{ + fmt.Sprintf("Accessor ID|%s", token.AccessorID), + fmt.Sprintf("Secret ID|%s", token.SecretID), + fmt.Sprintf("Name|%s", token.Name), + fmt.Sprintf("Type|%s", token.Type), + fmt.Sprintf("Global|%v", token.Global), + } + + // Special case the policy output + if token.Type == "management" { + output = append(output, "Policies|n/a") + } else { + output = append(output, fmt.Sprintf("Policies|%v", token.Policies)) + } + + // Add the generic output + output = append(output, + fmt.Sprintf("Create Time|%v", token.CreateTime), + fmt.Sprintf("Create Index|%d", token.CreateIndex), + fmt.Sprintf("Modify Index|%d", token.ModifyIndex), + ) + return formatKV(output) +} diff --git a/command/acl_bootstrap_test.go b/command/acl_bootstrap_test.go new file mode 100644 index 000000000..26b991f8c --- /dev/null +++ b/command/acl_bootstrap_test.go @@ -0,0 +1,12 @@ +package command + +import ( + "testing" + + "github.com/mitchellh/cli" +) + +func TestACLBootstrapCommand_Implements(t *testing.T) { + t.Parallel() + var _ cli.Command = &ACLBootstrapCommand{} +} diff --git a/commands.go b/commands.go index f9b596152..d45689244 100644 --- a/commands.go +++ b/commands.go @@ -26,6 +26,16 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory { } return map[string]cli.CommandFactory{ + "acl": func() (cli.Command, error) { + return &command.ACLCommand{ + Meta: meta, + }, nil + }, + "acl bootstrap": func() (cli.Command, error) { + return &command.ACLBootstrapCommand{ + Meta: meta, + }, nil + }, "alloc-status": func() (cli.Command, error) { return &command.AllocStatusCommand{ Meta: meta,