Update scopes CLI command in the model of the roles command (#262)
* Update scopes CLI command in the model of the roles command * Pull help functions to helper methods
This commit is contained in:
1 parent
f1d69ea1ee
commit
b75a6fc5e5
12 files changed
+442
-353
No files matched your search
@@ -61,6 +61,11 @@ type Command struct {
|
||||
FlagTokenName string
|
||||
flagOutputCurlString bool
|
||||
|
||||
FlagId string
|
||||
FlagName string
|
||||
FlagDescription string
|
||||
FlagVersion int
|
||||
|
||||
client *api.Client
|
||||
}
|
||||
|
||||
|
||||
@@ -59,13 +59,33 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
||||
}, nil
|
||||
},
|
||||
"scopes create": func() (cli.Command, error) {
|
||||
return &scopes.CreateScopeCommand{
|
||||
return &scopes.Command{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "create",
|
||||
}, nil
|
||||
},
|
||||
"scopes read": func() (cli.Command, error) {
|
||||
return &scopes.ReadScopeCommand{
|
||||
return &scopes.Command{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "read",
|
||||
}, nil
|
||||
},
|
||||
"scopes update": func() (cli.Command, error) {
|
||||
return &scopes.Command{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "update",
|
||||
}, nil
|
||||
},
|
||||
"scopes delete": func() (cli.Command, error) {
|
||||
return &scopes.Command{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "delete",
|
||||
}, nil
|
||||
},
|
||||
"scopes list": func() (cli.Command, error) {
|
||||
return &scopes.Command{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "list",
|
||||
}, nil
|
||||
},
|
||||
"config": func() (cli.Command, error) {
|
||||
|
||||
@@ -372,7 +372,7 @@ func (c *Command) ParseFlagsAndConfig(args []string) int {
|
||||
c.UI.Error(fmt.Sprintf("Invalid dev auth method ID, must start with %q", prefix))
|
||||
return 1
|
||||
}
|
||||
if len(c.flagDevAuthMethodId) != 13 {
|
||||
if len(c.flagDevAuthMethodId) != 15 {
|
||||
c.UI.Error(fmt.Sprintf("Invalid dev auth method ID, must be 10 base62 characters after %q", prefix))
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ func (c *Command) Run(args []string) int {
|
||||
c.UI.Error(fmt.Sprintf("Invalid dev auth method ID, must start with %q", prefix))
|
||||
return 1
|
||||
}
|
||||
if len(c.flagDevAuthMethodId) != 13 {
|
||||
if len(c.flagDevAuthMethodId) != 15 {
|
||||
c.UI.Error(fmt.Sprintf("Invalid dev auth method ID, must be 10 base62 characters after %q", prefix))
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -2,22 +2,16 @@ package roles
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/api/roles"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/cmd/common"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/mitchellh/go-wordwrap"
|
||||
)
|
||||
|
||||
func synopsisFunc(inFunc string) string {
|
||||
if inFunc == "" {
|
||||
return wordwrap.WrapString("Manage Boundary roles", base.TermWidth)
|
||||
}
|
||||
return wordwrap.WrapString(fmt.Sprintf("%s a role within Boundary", textproto.CanonicalMIMEHeaderKey(inFunc)), base.TermWidth)
|
||||
}
|
||||
|
||||
func principalsGrantsSynopsisFunc(inFunc string, principals bool) string {
|
||||
var in string
|
||||
switchStr := "principals (users, groups)"
|
||||
@@ -35,76 +29,6 @@ func principalsGrantsSynopsisFunc(inFunc string, principals bool) string {
|
||||
return wordwrap.WrapString(fmt.Sprintf("%s a role within Boundary", in), base.TermWidth)
|
||||
}
|
||||
|
||||
func baseHelp() string {
|
||||
return base.WrapForHelpText([]string{
|
||||
"Usage: boundary roles [sub command] [options] [args]",
|
||||
"",
|
||||
" This command allows operations on Boundary roles. Examples:",
|
||||
"",
|
||||
" Create a role:",
|
||||
"",
|
||||
` $ boundary roles create -name foo -description "For ProdOps usage"`,
|
||||
"",
|
||||
" Add a grant to a role:",
|
||||
"",
|
||||
` $ boundary roles add-grants -id r_1234567890 -grant "type=host-catalog;actions=create,delete"`,
|
||||
"",
|
||||
" Please see the role subcommand help for detailed usage information.",
|
||||
})
|
||||
}
|
||||
|
||||
func createHelp() string {
|
||||
return base.WrapForHelpText([]string{
|
||||
"Usage: boundary roles create [options] [args]",
|
||||
"",
|
||||
" Create a role. Example:",
|
||||
"",
|
||||
` $ boundary roles create -name ops -description "Role for ops grants"`,
|
||||
"",
|
||||
"",
|
||||
})
|
||||
}
|
||||
|
||||
func updateHelp() string {
|
||||
return base.WrapForHelpText([]string{
|
||||
"Usage: boundary roles update [options] [args]",
|
||||
"",
|
||||
" Update a role given its ID. Example:",
|
||||
"",
|
||||
` $ boundary roles update -id r_1234567890 -description "Development host grants"`,
|
||||
})
|
||||
}
|
||||
|
||||
func readHelp() string {
|
||||
return base.WrapForHelpText([]string{
|
||||
"Usage: boundary roles read [options] [args]",
|
||||
"",
|
||||
" Read a role given its ID. Example:",
|
||||
"",
|
||||
` $ boundary roles read -id r_1234567890`,
|
||||
})
|
||||
}
|
||||
|
||||
func deleteHelp() string {
|
||||
return base.WrapForHelpText([]string{
|
||||
"Usage: boundary roles delete [options] [args]",
|
||||
"",
|
||||
" Delete a role given its ID. Example:",
|
||||
"",
|
||||
` $ boundary roles delete -id r_1234567890`,
|
||||
})
|
||||
}
|
||||
|
||||
func listHelp() string {
|
||||
return base.WrapForHelpText([]string{
|
||||
"Usage: boundary roles list [options] [args]",
|
||||
"",
|
||||
" List roles within a scope. Example:",
|
||||
"",
|
||||
` $ boundary roles list -org o_1234567890`,
|
||||
})
|
||||
}
|
||||
|
||||
func addPrincipalsHelp() string {
|
||||
return base.WrapForHelpText([]string{
|
||||
"Usage: boundary roles add-principals [options] [args]",
|
||||
@@ -136,26 +60,10 @@ func removePrincipalsHelp() string {
|
||||
}
|
||||
|
||||
func populateFlags(c *Command, f *base.FlagSet, flagNames []string) {
|
||||
common.PopulateCommonFlags(c.Command, f, resource.Role, flagNames)
|
||||
|
||||
for _, name := range flagNames {
|
||||
switch name {
|
||||
case "id":
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "id",
|
||||
Target: &c.flagId,
|
||||
Usage: "ID of the role to operate on",
|
||||
})
|
||||
case "name":
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "name",
|
||||
Target: &c.flagName,
|
||||
Usage: "Name to set on the role",
|
||||
})
|
||||
case "description":
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "description",
|
||||
Target: &c.flagDescription,
|
||||
Usage: "Description to set on the role",
|
||||
})
|
||||
case "grantscopeid":
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "grant-scope-id",
|
||||
@@ -174,12 +82,6 @@ func populateFlags(c *Command, f *base.FlagSet, flagNames []string) {
|
||||
Target: &c.flagGrants,
|
||||
Usage: "The grants to add, remove, or set. May be specified multiple times. Can be in compact string format or JSON (be sure to escape JSON properly).",
|
||||
})
|
||||
case "version":
|
||||
f.IntVar(&base.IntVar{
|
||||
Name: "version",
|
||||
Target: &c.flagVersion,
|
||||
Usage: "The version of the resource against which to perform an update operation. If not specified, the command will perform a check-and-set automatically.",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/roles"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/cmd/common"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
"github.com/kr/pretty"
|
||||
"github.com/mitchellh/cli"
|
||||
@@ -21,39 +23,32 @@ type Command struct {
|
||||
|
||||
Func string
|
||||
|
||||
flagId string
|
||||
flagName string
|
||||
flagDescription string
|
||||
flagGrantScopeId string
|
||||
flagPrincipals []string
|
||||
flagGrants []string
|
||||
flagVersion int
|
||||
}
|
||||
|
||||
func (c *Command) Synopsis() string {
|
||||
switch c.Func {
|
||||
case "create", "update", "read", "delete", "list":
|
||||
return synopsisFunc(c.Func)
|
||||
case "", "create", "update", "read", "delete", "list":
|
||||
return common.SynopsisFunc(c.Func, "role")
|
||||
case "add-principals", "set-principals", "remove-principals":
|
||||
return principalsGrantsSynopsisFunc(c.Func, true)
|
||||
case "add-grants", "set-grants", "remove-grants":
|
||||
return principalsGrantsSynopsisFunc(c.Func, false)
|
||||
}
|
||||
return "Manage Boundary roles"
|
||||
return ""
|
||||
}
|
||||
|
||||
var helpMap = map[string]func() string{
|
||||
"create": createHelp,
|
||||
"update": updateHelp,
|
||||
"read": readHelp,
|
||||
"delete": deleteHelp,
|
||||
"list": listHelp,
|
||||
"add-principals": addPrincipalsHelp,
|
||||
"set-principals": setPrincipalsHelp,
|
||||
"remove-principals": removePrincipalsHelp,
|
||||
"add-grants": addPrincipalsHelp,
|
||||
"set-grants": setPrincipalsHelp,
|
||||
"remove-grants": removePrincipalsHelp,
|
||||
var helpMap = func() map[string]func() string {
|
||||
ret := common.HelpMap(resource.Role)
|
||||
ret["add-principals"] = addPrincipalsHelp
|
||||
ret["set-principals"] = setPrincipalsHelp
|
||||
ret["remove-principals"] = removePrincipalsHelp
|
||||
ret["add-grants"] = addPrincipalsHelp
|
||||
ret["set-grants"] = setPrincipalsHelp
|
||||
ret["remove-grants"] = removePrincipalsHelp
|
||||
return ret
|
||||
}
|
||||
|
||||
var flagsMap = map[string][]string{
|
||||
@@ -70,10 +65,11 @@ var flagsMap = map[string][]string{
|
||||
}
|
||||
|
||||
func (c *Command) Help() string {
|
||||
hm := helpMap()
|
||||
if c.Func == "" {
|
||||
return baseHelp()
|
||||
return hm["base"]()
|
||||
}
|
||||
return helpMap[c.Func]() + "\n\n" + c.Flags().Help()
|
||||
return hm[c.Func]() + "\n\n" + c.Flags().Help()
|
||||
}
|
||||
|
||||
func (c *Command) Flags() *base.FlagSets {
|
||||
@@ -105,7 +101,7 @@ func (c *Command) Run(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
if strutil.StrListContains(flagsMap[c.Func], "id") && c.flagId == "" {
|
||||
if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" {
|
||||
c.UI.Error("ID is required but not passed in via -id")
|
||||
return 1
|
||||
}
|
||||
@@ -118,19 +114,19 @@ func (c *Command) Run(args []string) int {
|
||||
|
||||
var opts []roles.Option
|
||||
|
||||
switch c.flagName {
|
||||
switch c.FlagName {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, roles.DefaultName())
|
||||
default:
|
||||
opts = append(opts, roles.WithName(c.flagName))
|
||||
opts = append(opts, roles.WithName(c.FlagName))
|
||||
}
|
||||
switch c.flagDescription {
|
||||
switch c.FlagDescription {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, roles.DefaultDescription())
|
||||
default:
|
||||
opts = append(opts, roles.WithDescription(c.flagDescription))
|
||||
opts = append(opts, roles.WithDescription(c.FlagDescription))
|
||||
}
|
||||
switch c.flagGrantScopeId {
|
||||
case "":
|
||||
@@ -200,11 +196,11 @@ func (c *Command) Run(args []string) int {
|
||||
case "create", "read", "delete", "list":
|
||||
// These don't udpate so don't need the existing version
|
||||
default:
|
||||
switch c.flagVersion {
|
||||
switch c.FlagVersion {
|
||||
case 0:
|
||||
opts = append(opts, roles.WithAutomaticVersioning())
|
||||
default:
|
||||
version = uint32(c.flagVersion)
|
||||
version = uint32(c.FlagVersion)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,25 +213,25 @@ func (c *Command) Run(args []string) int {
|
||||
case "create":
|
||||
role, apiErr, err = roleClient.Create(c.Context, opts...)
|
||||
case "update":
|
||||
role, apiErr, err = roleClient.Update(c.Context, c.flagId, version, opts...)
|
||||
role, apiErr, err = roleClient.Update(c.Context, c.FlagId, version, opts...)
|
||||
case "read":
|
||||
role, apiErr, err = roleClient.Read(c.Context, c.flagId, opts...)
|
||||
role, apiErr, err = roleClient.Read(c.Context, c.FlagId, opts...)
|
||||
case "delete":
|
||||
existed, apiErr, err = roleClient.Delete(c.Context, c.flagId, opts...)
|
||||
existed, apiErr, err = roleClient.Delete(c.Context, c.FlagId, opts...)
|
||||
case "list":
|
||||
listedRoles, apiErr, err = roleClient.List(c.Context, opts...)
|
||||
case "add-principals":
|
||||
role, apiErr, err = roleClient.AddPrincipals(c.Context, c.flagId, version, principals, opts...)
|
||||
role, apiErr, err = roleClient.AddPrincipals(c.Context, c.FlagId, version, principals, opts...)
|
||||
case "set-principals":
|
||||
role, apiErr, err = roleClient.SetPrincipals(c.Context, c.flagId, version, principals, opts...)
|
||||
role, apiErr, err = roleClient.SetPrincipals(c.Context, c.FlagId, version, principals, opts...)
|
||||
case "remove-principals":
|
||||
role, apiErr, err = roleClient.RemovePrincipals(c.Context, c.flagId, version, principals, opts...)
|
||||
role, apiErr, err = roleClient.RemovePrincipals(c.Context, c.FlagId, version, principals, opts...)
|
||||
case "add-grants":
|
||||
role, apiErr, err = roleClient.AddGrants(c.Context, c.flagId, version, grants, opts...)
|
||||
role, apiErr, err = roleClient.AddGrants(c.Context, c.FlagId, version, grants, opts...)
|
||||
case "set-grants":
|
||||
role, apiErr, err = roleClient.SetGrants(c.Context, c.flagId, version, grants, opts...)
|
||||
role, apiErr, err = roleClient.SetGrants(c.Context, c.FlagId, version, grants, opts...)
|
||||
case "remove-grants":
|
||||
role, apiErr, err = roleClient.RemoveGrants(c.Context, c.flagId, version, grants, opts...)
|
||||
role, apiErr, err = roleClient.RemoveGrants(c.Context, c.FlagId, version, grants, opts...)
|
||||
}
|
||||
|
||||
plural := "role"
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
package scopes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/api/scopes"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/kr/pretty"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
var _ cli.Command = (*CreateScopeCommand)(nil)
|
||||
var _ cli.CommandAutocomplete = (*CreateScopeCommand)(nil)
|
||||
|
||||
type CreateScopeCommand struct {
|
||||
*base.Command
|
||||
|
||||
flagName string
|
||||
flagDescription string
|
||||
}
|
||||
|
||||
func (c *CreateScopeCommand) Synopsis() string {
|
||||
return "Creates a scope within a parent scope"
|
||||
}
|
||||
|
||||
func (c *CreateScopeCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: boundary scopes create
|
||||
|
||||
Creates a new scope within the scope specified by the ID from the
|
||||
"scope" parameter or the associated environment variable.
|
||||
|
||||
Example:
|
||||
|
||||
$ boundary scopes create -scope=<scope_id> -name=<name>
|
||||
|
||||
` + c.Flags().Help()
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *CreateScopeCommand) Flags() *base.FlagSets {
|
||||
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "name",
|
||||
Target: &c.flagName,
|
||||
Completion: complete.PredictAnything,
|
||||
Usage: "An optional name assigned to the project for display purposes",
|
||||
})
|
||||
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "description",
|
||||
Target: &c.flagDescription,
|
||||
Completion: complete.PredictNothing,
|
||||
Usage: "An optional description assigned to the project for display purposes",
|
||||
})
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *CreateScopeCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictAnything
|
||||
}
|
||||
|
||||
func (c *CreateScopeCommand) AutocompleteFlags() complete.Flags {
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *CreateScopeCommand) Run(args []string) int {
|
||||
f := c.Flags()
|
||||
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 2
|
||||
}
|
||||
|
||||
scp, apiErr, err := scopes.NewScopesClient(client).Create(c.Context,
|
||||
client.ScopeId(),
|
||||
scopes.WithName(c.flagName),
|
||||
scopes.WithDescription(c.flagDescription))
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
c.UI.Error(fmt.Errorf("error creating project: %w", err).Error())
|
||||
return 2
|
||||
case apiErr != nil:
|
||||
c.UI.Error(pretty.Sprint(apiErr))
|
||||
return 2
|
||||
}
|
||||
|
||||
switch base.Format(c.UI) {
|
||||
case "table":
|
||||
c.UI.Output(printScope(scp))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
)
|
||||
|
||||
func printScope(in *scopes.Scope) string {
|
||||
func generateScopeTableOutput(in *scopes.Scope) string {
|
||||
var ret []string
|
||||
// This if true is here to line up columns for easy editing
|
||||
if true {
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
package scopes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/api/scopes"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/kr/pretty"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
var _ cli.Command = (*ReadScopeCommand)(nil)
|
||||
var _ cli.CommandAutocomplete = (*ReadScopeCommand)(nil)
|
||||
|
||||
type ReadScopeCommand struct {
|
||||
*base.Command
|
||||
|
||||
flagId string
|
||||
}
|
||||
|
||||
func (c *ReadScopeCommand) Synopsis() string {
|
||||
return "Reads a scope's data"
|
||||
}
|
||||
|
||||
func (c *ReadScopeCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: boundary scopes read
|
||||
|
||||
Returns information about a scope specified by the ID. The request will take place within the scope of the caller's authentication token.
|
||||
|
||||
Example:
|
||||
|
||||
$ boundary scopes read -id=<scope_id>
|
||||
|
||||
` + c.Flags().Help()
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *ReadScopeCommand) Flags() *base.FlagSets {
|
||||
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "id",
|
||||
Target: &c.flagId,
|
||||
Completion: complete.PredictNothing,
|
||||
Usage: "The ID of the scope to read",
|
||||
})
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *ReadScopeCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictAnything
|
||||
}
|
||||
|
||||
func (c *ReadScopeCommand) AutocompleteFlags() complete.Flags {
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *ReadScopeCommand) Run(args []string) int {
|
||||
f := c.Flags()
|
||||
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 2
|
||||
}
|
||||
|
||||
if len(c.flagId) == 0 {
|
||||
c.UI.Error("No scope ID supplied via -id")
|
||||
return 1
|
||||
}
|
||||
|
||||
scp, apiErr, err := scopes.NewScopesClient(client).Read(c.Context, c.flagId)
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
c.UI.Error(fmt.Errorf("error reading project: %w", err).Error())
|
||||
return 2
|
||||
case apiErr != nil:
|
||||
c.UI.Error(pretty.Sprint(apiErr))
|
||||
return 2
|
||||
}
|
||||
|
||||
switch base.Format(c.UI) {
|
||||
case "table":
|
||||
c.UI.Output(printScope(scp))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package scopes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/scopes"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/cmd/common"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
"github.com/kr/pretty"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
var _ cli.Command = (*Command)(nil)
|
||||
var _ cli.CommandAutocomplete = (*Command)(nil)
|
||||
|
||||
type Command struct {
|
||||
*base.Command
|
||||
|
||||
Func string
|
||||
}
|
||||
|
||||
func (c *Command) Synopsis() string {
|
||||
return common.SynopsisFunc(c.Func, "scope")
|
||||
}
|
||||
|
||||
var flagsMap = map[string][]string{
|
||||
"create": {"name", "description"},
|
||||
"update": {"id", "name", "description", "version"},
|
||||
"read": {"id"},
|
||||
"delete": {"id"},
|
||||
}
|
||||
|
||||
func (c *Command) Help() string {
|
||||
helpMap := common.HelpMap(resource.Scope)
|
||||
if c.Func == "" {
|
||||
return helpMap["base"]()
|
||||
}
|
||||
return helpMap[c.Func]() + "\n\n" + c.Flags().Help()
|
||||
}
|
||||
|
||||
func (c *Command) Flags() *base.FlagSets {
|
||||
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
common.PopulateCommonFlags(c.Command, f, resource.Scope, flagsMap[c.Func])
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *Command) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictAnything
|
||||
}
|
||||
|
||||
func (c *Command) AutocompleteFlags() complete.Flags {
|
||||
return c.Flags().Completions()
|
||||
}
|
||||
|
||||
func (c *Command) Run(args []string) int {
|
||||
if c.Func == "" {
|
||||
return cli.RunResultHelp
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
if strutil.StrListContains(flagsMap[c.Func], "id") && c.FlagId == "" {
|
||||
c.UI.Error("ID is required but not passed in via -id")
|
||||
return 1
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error creating API client: %s", err.Error()))
|
||||
return 2
|
||||
}
|
||||
|
||||
var opts []scopes.Option
|
||||
|
||||
switch c.FlagName {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, scopes.DefaultName())
|
||||
default:
|
||||
opts = append(opts, scopes.WithName(c.FlagName))
|
||||
}
|
||||
|
||||
switch c.FlagDescription {
|
||||
case "":
|
||||
case "null":
|
||||
opts = append(opts, scopes.DefaultDescription())
|
||||
default:
|
||||
opts = append(opts, scopes.WithDescription(c.FlagDescription))
|
||||
}
|
||||
|
||||
scopeClient := scopes.NewScopesClient(client)
|
||||
|
||||
// Perform check-and-set when needed
|
||||
var version uint32
|
||||
switch c.Func {
|
||||
case "create", "read", "delete", "list":
|
||||
// These don't udpate so don't need the existing version
|
||||
default:
|
||||
switch c.FlagVersion {
|
||||
case 0:
|
||||
opts = append(opts, scopes.WithAutomaticVersioning())
|
||||
default:
|
||||
version = uint32(c.FlagVersion)
|
||||
}
|
||||
}
|
||||
|
||||
var existed bool
|
||||
var scope *scopes.Scope
|
||||
var listedScopes []*scopes.Scope
|
||||
var apiErr *api.Error
|
||||
|
||||
switch c.Func {
|
||||
case "create":
|
||||
scope, apiErr, err = scopeClient.Create(c.Context, client.ScopeId(), opts...)
|
||||
case "update":
|
||||
scope, apiErr, err = scopeClient.Update(c.Context, c.FlagId, version, opts...)
|
||||
case "read":
|
||||
scope, apiErr, err = scopeClient.Read(c.Context, c.FlagId, opts...)
|
||||
case "delete":
|
||||
existed, apiErr, err = scopeClient.Delete(c.Context, c.FlagId, opts...)
|
||||
case "list":
|
||||
listedScopes, apiErr, err = scopeClient.List(c.Context, client.ScopeId(), opts...)
|
||||
}
|
||||
|
||||
plural := "scope"
|
||||
if c.Func == "list" {
|
||||
plural = "scopes"
|
||||
}
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error trying to %s %s: %s", c.Func, plural, err.Error()))
|
||||
return 2
|
||||
}
|
||||
if apiErr != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error from controller when performing %s on %s: %s", c.Func, plural, pretty.Sprint(apiErr)))
|
||||
return 1
|
||||
}
|
||||
|
||||
switch c.Func {
|
||||
case "delete":
|
||||
switch base.Format(c.UI) {
|
||||
case "json":
|
||||
c.UI.Output("null")
|
||||
case "table":
|
||||
output := "The delete operation completed successfully"
|
||||
switch existed {
|
||||
case true:
|
||||
output += "."
|
||||
default:
|
||||
output += ", however the resource did not exist at the time."
|
||||
}
|
||||
c.UI.Output(output)
|
||||
}
|
||||
return 0
|
||||
|
||||
case "list":
|
||||
switch base.Format(c.UI) {
|
||||
case "json":
|
||||
if len(listedScopes) == 0 {
|
||||
c.UI.Output("null")
|
||||
return 0
|
||||
}
|
||||
b, err := base.JsonFormatter{}.Format(listedScopes)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error formatting to JSON: %w", err).Error())
|
||||
return 1
|
||||
}
|
||||
c.UI.Output(string(b))
|
||||
|
||||
case "table":
|
||||
if len(listedScopes) == 0 {
|
||||
c.UI.Output("No scopes found")
|
||||
return 0
|
||||
}
|
||||
var output []string
|
||||
output = []string{
|
||||
"",
|
||||
"Scope information:",
|
||||
}
|
||||
for i, s := range listedScopes {
|
||||
if i > 0 {
|
||||
output = append(output, "")
|
||||
}
|
||||
if true {
|
||||
output = append(output,
|
||||
fmt.Sprintf(" ID: %s", s.Id),
|
||||
)
|
||||
}
|
||||
if s.Name != "" {
|
||||
output = append(output,
|
||||
fmt.Sprintf(" Name: %s", s.Name),
|
||||
)
|
||||
}
|
||||
if s.Description != "" {
|
||||
output = append(output,
|
||||
fmt.Sprintf(" Description: %s", s.Description),
|
||||
)
|
||||
}
|
||||
}
|
||||
c.UI.Output(base.WrapForHelpText(output))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
switch base.Format(c.UI) {
|
||||
case "table":
|
||||
c.UI.Output(generateScopeTableOutput(scope))
|
||||
case "json":
|
||||
b, err := base.JsonFormatter{}.Format(scope)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error formatting to JSON: %w", err).Error())
|
||||
return 1
|
||||
}
|
||||
c.UI.Output(string(b))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
)
|
||||
|
||||
func PopulateCommonFlags(c *base.Command, f *base.FlagSet, resourceType resource.Type, flagNames []string) {
|
||||
for _, name := range flagNames {
|
||||
switch name {
|
||||
case "id":
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "id",
|
||||
Target: &c.FlagId,
|
||||
Usage: fmt.Sprintf("ID of the %s on which to operate", resourceType.String()),
|
||||
})
|
||||
case "name":
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "name",
|
||||
Target: &c.FlagName,
|
||||
Usage: fmt.Sprintf("Name to set on the %s", resourceType.String()),
|
||||
})
|
||||
case "description":
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "description",
|
||||
Target: &c.FlagDescription,
|
||||
Usage: fmt.Sprintf("Description to set on the %s", resourceType.String()),
|
||||
})
|
||||
case "version":
|
||||
f.IntVar(&base.IntVar{
|
||||
Name: "version",
|
||||
Target: &c.FlagVersion,
|
||||
Usage: fmt.Sprintf("The version of the %s against which to perform an update operation. If not specified, the command will perform a check-and-set automatically.", resourceType.String()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/mitchellh/go-wordwrap"
|
||||
)
|
||||
|
||||
func SynopsisFunc(inFunc, resourceType string) string {
|
||||
if inFunc == "" {
|
||||
return wordwrap.WrapString(fmt.Sprintf("Manage Boundary %ss", resourceType), base.TermWidth)
|
||||
}
|
||||
return wordwrap.WrapString(fmt.Sprintf("%s a %s within Boundary", textproto.CanonicalMIMEHeaderKey(inFunc), resourceType), base.TermWidth)
|
||||
}
|
||||
|
||||
func HelpMap(resourceType resource.Type) map[string]func() string {
|
||||
prefixMap := map[string]string{
|
||||
resource.Scope.String(): "o",
|
||||
resource.Role.String(): "r",
|
||||
resource.Group.String(): "g",
|
||||
resource.User.String(): "u",
|
||||
}
|
||||
return map[string]func() string{
|
||||
"base": func() string {
|
||||
return base.WrapForHelpText(subType([]string{
|
||||
"Usage: boundary {{type}}s [sub command] [options] [args]",
|
||||
"",
|
||||
" This command allows operations on Boundary {{type}}s. Examples",
|
||||
"",
|
||||
" Create a {{type}}:",
|
||||
"",
|
||||
` $ boundary {{type}}s create -name prodops -description "For ProdOps usage"`,
|
||||
"",
|
||||
" Please see the {{type}}s subcommand help for detailed usage information.",
|
||||
}, resourceType, prefixMap))
|
||||
},
|
||||
|
||||
"create": func() string {
|
||||
return base.WrapForHelpText(subType([]string{
|
||||
"Usage: boundary {{type}}s create [options] [args]",
|
||||
"",
|
||||
" Create a {{type}}. Example:",
|
||||
"",
|
||||
` $ boundary {{type}}s create -name prodops -description "{{uppertype}} for ProdOps"`,
|
||||
"",
|
||||
"",
|
||||
}, resourceType, prefixMap))
|
||||
},
|
||||
|
||||
"update": func() string {
|
||||
return base.WrapForHelpText(subType([]string{
|
||||
"Usage: boundary {{type}}s update [options] [args]",
|
||||
"",
|
||||
" Update a {{type}} given its ID. Example:",
|
||||
"",
|
||||
` $ boundary {{type}}s update -id {{prefix}}_1234567890 -name "devops" -description "{{uppertype}} for DevOps"`,
|
||||
}, resourceType, prefixMap))
|
||||
},
|
||||
|
||||
"read": func() string {
|
||||
return base.WrapForHelpText(subType([]string{
|
||||
"Usage: boundary {{type}}s read [options] [args]",
|
||||
"",
|
||||
" Read a {{type}} given its ID. Example:",
|
||||
"",
|
||||
` $ boundary {{type}}s read -id {{prefix}}_1234567890`,
|
||||
}, resourceType, prefixMap))
|
||||
},
|
||||
|
||||
"delete": func() string {
|
||||
return base.WrapForHelpText(subType([]string{
|
||||
"Usage: boundary {{type}}s delete [options] [args]",
|
||||
"",
|
||||
" Delete a {{type}} given its ID. Example:",
|
||||
"",
|
||||
` $ boundary {{type}}s delete -id {{prefix}}_1234567890`,
|
||||
}, resourceType, prefixMap))
|
||||
},
|
||||
|
||||
"list": func() string {
|
||||
return base.WrapForHelpText(subType([]string{
|
||||
"Usage: boundary {{type}}s list [options] [args]",
|
||||
"",
|
||||
" List {{type}}s within an enclosing scope or resource. Example:",
|
||||
"",
|
||||
` $ boundary {{type}}s list`,
|
||||
}, resourceType, prefixMap))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func subType(in []string, resType resource.Type, prefixMap map[string]string) []string {
|
||||
for i, v := range in {
|
||||
in[i] =
|
||||
strings.Replace(
|
||||
strings.Replace(
|
||||
strings.Replace(
|
||||
v, "{{type}}", resType.String(), -1),
|
||||
"{{uppertype}}", textproto.CanonicalMIMEHeaderKey(resType.String()), -1),
|
||||
"{{prefix}}", prefixMap[resType.String()], -1)
|
||||
}
|
||||
return in
|
||||
}
|
||||
Reference in new issue
Block a user