diff --git a/command/commands.go b/command/commands.go index ba83d6cfc5..b8fb6aaf8f 100644 --- a/command/commands.go +++ b/command/commands.go @@ -321,6 +321,31 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { Handlers: loginHandlers, }, nil }, + "namespace": func() (cli.Command, error) { + return &NamespaceCommand{ + BaseCommand: getBaseCommand(), + }, nil + }, + "namespace list": func() (cli.Command, error) { + return &NamespaceListCommand{ + BaseCommand: getBaseCommand(), + }, nil + }, + "namespace lookup": func() (cli.Command, error) { + return &NamespaceLookupCommand{ + BaseCommand: getBaseCommand(), + }, nil + }, + "namespace create": func() (cli.Command, error) { + return &NamespaceCreateCommand{ + BaseCommand: getBaseCommand(), + }, nil + }, + "namespace delete": func() (cli.Command, error) { + return &NamespaceDeleteCommand{ + BaseCommand: getBaseCommand(), + }, nil + }, "operator": func() (cli.Command, error) { return &OperatorCommand{ BaseCommand: getBaseCommand(), diff --git a/command/namespace.go b/command/namespace.go new file mode 100644 index 0000000000..1f232ef157 --- /dev/null +++ b/command/namespace.go @@ -0,0 +1,51 @@ +package command + +import ( + "strings" + + "github.com/mitchellh/cli" +) + +var _ cli.Command = (*NamespaceCommand)(nil) + +type NamespaceCommand struct { + *BaseCommand +} + +func (c *NamespaceCommand) Synopsis() string { + return "Interact with namespaces" +} + +func (c *NamespaceCommand) Help() string { + helpText := ` +Usage: vault namespace [options] [args] + + This command groups subcommands for interacting with Vault namespaces. + These set of subcommands operate on the context of the namespace that the + current logged in token belongs to. + + List enabled child namespaces: + + $ vault namespace list + + Look up an existing namespace: + + $ vault namespace lookup + + Create a new namespace: + + $ vault namespace create + + Delete an existing namespace: + + $ vault namespace delete + + Please see the individual subcommand help for detailed usage information. +` + + return strings.TrimSpace(helpText) +} + +func (c *NamespaceCommand) Run(args []string) int { + return cli.RunResultHelp +} diff --git a/command/namespace_create.go b/command/namespace_create.go new file mode 100644 index 0000000000..3097215c5b --- /dev/null +++ b/command/namespace_create.go @@ -0,0 +1,92 @@ +package command + +import ( + "fmt" + "path" + "strings" + + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*NamespaceCreateCommand)(nil) +var _ cli.CommandAutocomplete = (*NamespaceCreateCommand)(nil) + +type NamespaceCreateCommand struct { + *BaseCommand +} + +func (c *NamespaceCreateCommand) Synopsis() string { + return "Create a new namespace" +} + +func (c *NamespaceCreateCommand) Help() string { + helpText := ` +Usage: vault namespace create [options] PATH + + Create a child namespace. The namespace created will be relative to the + namespace provided in either VAULT_NAMESPACE environemnt variable or + -namespace CLI flag. + + Create a child namespace (e.g. ns1/): + + $ vault namespace create ns1 + + Create a child namespace from a parent namespace (e.g. ns1/ns2/): + + $ vault namespace create -namespace=ns1 ns2 + +` + c.Flags().Help() + + return strings.TrimSpace(helpText) +} + +func (c *NamespaceCreateCommand) Flags() *FlagSets { + return c.flagSet(FlagSetHTTP) +} + +func (c *NamespaceCreateCommand) AutocompleteArgs() complete.Predictor { + return c.PredictVaultFolders() +} + +func (c *NamespaceCreateCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *NamespaceCreateCommand) Run(args []string) int { + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + args = f.Args() + switch { + case len(args) < 1: + c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args))) + return 1 + case len(args) > 1: + c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) + return 1 + } + + namespacePath := strings.TrimSpace(args[0]) + + client, err := c.Client() + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + + err = client.Sys().CreateNamespace(namespacePath) + if err != nil { + c.UI.Error(fmt.Sprintf("Error creating namespace: %s", err)) + return 2 + } + + // Output full path + fullPath := path.Join(c.flagNamespace, namespacePath) + "/" + c.UI.Output(fmt.Sprintf("Success! Namespace created at: %s", fullPath)) + return 0 +} diff --git a/command/namespace_delete.go b/command/namespace_delete.go new file mode 100644 index 0000000000..3565d1b816 --- /dev/null +++ b/command/namespace_delete.go @@ -0,0 +1,92 @@ +package command + +import ( + "fmt" + "path" + "strings" + + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*NamespaceDeleteCommand)(nil) +var _ cli.CommandAutocomplete = (*NamespaceDeleteCommand)(nil) + +type NamespaceDeleteCommand struct { + *BaseCommand +} + +func (c *NamespaceDeleteCommand) Synopsis() string { + return "Delete an existing namespace" +} + +func (c *NamespaceDeleteCommand) Help() string { + helpText := ` +Usage: vault namespace delete [options] PATH + + Delete an existing namespace. The namespace deleted will be relative to the + namespace provided in either VAULT_NAMESPACE environemnt variable or + -namespace CLI flag. + + Delete a namespace (e.g. ns1/): + + $ vault namespace delete ns1 + + Delete a namespace namespace from a parent namespace (e.g. ns1/ns2/): + + $ vault namespace create -namespace=ns1 ns2 + +` + c.Flags().Help() + + return strings.TrimSpace(helpText) +} + +func (c *NamespaceDeleteCommand) Flags() *FlagSets { + return c.flagSet(FlagSetHTTP) +} + +func (c *NamespaceDeleteCommand) AutocompleteArgs() complete.Predictor { + return c.PredictVaultFolders() +} + +func (c *NamespaceDeleteCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *NamespaceDeleteCommand) Run(args []string) int { + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + args = f.Args() + switch { + case len(args) < 1: + c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args))) + return 1 + case len(args) > 1: + c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) + return 1 + } + + namespacePath := strings.TrimSpace(args[0]) + + client, err := c.Client() + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + + err = client.Sys().DeleteNamespace(namespacePath) + if err != nil { + c.UI.Error(fmt.Sprintf("Error deleting namespace: %s", err)) + return 2 + } + + // Output full path + fullPath := path.Join(c.flagNamespace, namespacePath) + "/" + c.UI.Output(fmt.Sprintf("Success! Namespace deleted at: %s", fullPath)) + return 0 +} diff --git a/command/namespace_list.go b/command/namespace_list.go new file mode 100644 index 0000000000..5ba3248faf --- /dev/null +++ b/command/namespace_list.go @@ -0,0 +1,84 @@ +package command + +import ( + "fmt" + "strings" + + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*NamespaceListCommand)(nil) +var _ cli.CommandAutocomplete = (*NamespaceListCommand)(nil) + +type NamespaceListCommand struct { + *BaseCommand +} + +func (c *NamespaceListCommand) Synopsis() string { + return "List child namespaces" +} + +func (c *NamespaceListCommand) Help() string { + helpText := ` +Usage: vault namespaces list [options] + + Lists the enabled child namespaces. + + List all enabled child namespaces: + + $ vault namespaces list + +` + c.Flags().Help() + + return strings.TrimSpace(helpText) +} + +func (c *NamespaceListCommand) Flags() *FlagSets { + return c.flagSet(FlagSetHTTP | FlagSetOutputFormat) +} + +func (c *NamespaceListCommand) AutocompleteArgs() complete.Predictor { + return c.PredictVaultFolders() +} + +func (c *NamespaceListCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *NamespaceListCommand) Run(args []string) int { + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + args = f.Args() + if len(args) > 0 { + c.UI.Error(fmt.Sprintf("Too many arguments (expected 0, got %d)", len(args))) + return 1 + } + + client, err := c.Client() + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + + namespaces, err := client.Sys().ListNamespaces() + if err != nil { + c.UI.Error(fmt.Sprintf("Error listing namespaces: %s", err)) + return 2 + } + + switch Format(c.UI) { + case "table": + for _, ns := range namespaces.NamespacePaths { + c.UI.Output(ns) + } + return 0 + default: + return OutputData(c.UI, namespaces) + } +} diff --git a/command/namespace_lookup.go b/command/namespace_lookup.go new file mode 100644 index 0000000000..0e913becfa --- /dev/null +++ b/command/namespace_lookup.go @@ -0,0 +1,96 @@ +package command + +import ( + "fmt" + "strings" + + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var _ cli.Command = (*NamespaceLookupCommand)(nil) +var _ cli.CommandAutocomplete = (*NamespaceLookupCommand)(nil) + +type NamespaceLookupCommand struct { + *BaseCommand +} + +func (c *NamespaceLookupCommand) Synopsis() string { + return "Create a new namespace" +} + +func (c *NamespaceLookupCommand) Help() string { + helpText := ` +Usage: vault namespace create [options] PATH + + Create a child namespace. The namespace created will be relative to the + namespace provided in either VAULT_NAMESPACE environemnt variable or + -namespace CLI flag. + + Get information about the namespace of the locally authenticated token: + + $ vault namespace lookup + + Get information about the namespace of a particular child token (e.g. ns1/ns2/): + + $ vault namespace create -namespace=ns1 ns2 + +` + c.Flags().Help() + + return strings.TrimSpace(helpText) +} + +func (c *NamespaceLookupCommand) Flags() *FlagSets { + return c.flagSet(FlagSetHTTP | FlagSetOutputFormat) +} + +func (c *NamespaceLookupCommand) AutocompleteArgs() complete.Predictor { + return c.PredictVaultFolders() +} + +func (c *NamespaceLookupCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *NamespaceLookupCommand) Run(args []string) int { + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + args = f.Args() + switch { + case len(args) < 1: + c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args))) + return 1 + case len(args) > 1: + c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) + return 1 + } + + namespacePath := strings.TrimSpace(args[0]) + + client, err := c.Client() + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + + resp, err := client.Sys().GetNamespace(namespacePath) + if err != nil { + c.UI.Error(fmt.Sprintf("Error looking up namespace: %s", err)) + return 2 + } + + switch Format(c.UI) { + case "table": + data := map[string]interface{}{ + "path": resp.Path, + } + return OutputData(c.UI, data) + default: + return OutputData(c.UI, resp) + } +}