diff --git a/.changelog/9468.txt b/.changelog/9468.txt new file mode 100644 index 000000000..80bfc591e --- /dev/null +++ b/.changelog/9468.txt @@ -0,0 +1,3 @@ +```release-note:feature +cli: The `consul intention` command now has a new `list` subcommand to allow the listing of configured intentions. It also supports the `-namespace=` option. +``` diff --git a/command/commands_oss.go b/command/commands_oss.go index d8e42f642..781f74412 100644 --- a/command/commands_oss.go +++ b/command/commands_oss.go @@ -64,6 +64,7 @@ import ( ixncreate "github.com/hashicorp/consul/command/intention/create" ixndelete "github.com/hashicorp/consul/command/intention/delete" ixnget "github.com/hashicorp/consul/command/intention/get" + ixnlist "github.com/hashicorp/consul/command/intention/list" ixnmatch "github.com/hashicorp/consul/command/intention/match" "github.com/hashicorp/consul/command/join" "github.com/hashicorp/consul/command/keygen" @@ -182,6 +183,7 @@ func init() { Register("intention create", func(ui cli.Ui) (cli.Command, error) { return ixncreate.New(ui), nil }) Register("intention delete", func(ui cli.Ui) (cli.Command, error) { return ixndelete.New(ui), nil }) Register("intention get", func(ui cli.Ui) (cli.Command, error) { return ixnget.New(ui), nil }) + Register("intention list", func(ui cli.Ui) (cli.Command, error) { return ixnlist.New(ui), nil }) Register("intention match", func(ui cli.Ui) (cli.Command, error) { return ixnmatch.New(ui), nil }) Register("join", func(ui cli.Ui) (cli.Command, error) { return join.New(ui), nil }) Register("keygen", func(ui cli.Ui) (cli.Command, error) { return keygen.New(ui), nil }) diff --git a/command/intention/intention.go b/command/intention/intention.go index 94de26472..44cc7db42 100644 --- a/command/intention/intention.go +++ b/command/intention/intention.go @@ -40,6 +40,10 @@ Usage: consul intention [options] [args] $ consul intention check web db + List all intentions: + + $ consul intention list + Find all intentions for communicating to the "db" service: $ consul intention match db diff --git a/command/intention/list/intention_list.go b/command/intention/list/intention_list.go new file mode 100644 index 000000000..929c99cb0 --- /dev/null +++ b/command/intention/list/intention_list.go @@ -0,0 +1,85 @@ +package list + +import ( + "flag" + "fmt" + + "github.com/hashicorp/consul/command/flags" + "github.com/mitchellh/cli" + "github.com/ryanuber/columnize" +) + +func New(ui cli.Ui) *cmd { + c := &cmd{UI: ui} + c.init() + return c +} + +type cmd struct { + UI cli.Ui + flags *flag.FlagSet + http *flags.HTTPFlags + help string +} + +func (c *cmd) init() { + c.flags = flag.NewFlagSet("", flag.ContinueOnError) + + c.http = &flags.HTTPFlags{} + flags.Merge(c.flags, c.http.ClientFlags()) + flags.Merge(c.flags, c.http.ServerFlags()) + flags.Merge(c.flags, c.http.NamespaceFlags()) + c.help = flags.Usage(help, c.flags) +} + +func (c *cmd) Run(args []string) int { + if err := c.flags.Parse(args); err != nil { + return 1 + } + + client, err := c.http.APIClient() + if err != nil { + c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) + return 1 + } + + ixns, _, err := client.Connect().Intentions(nil) + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to retrieve the intentions list: %s", err)) + return 1 + } + + if len(ixns) == 0 { + c.UI.Error(fmt.Sprintf("There are no intentions.")) + return 2 + } + + result := make([]string, 0, len(ixns)) + header := "ID\x1fSource\x1fAction\x1fDestination\x1fPrecedence" + result = append(result, header) + for _, ixn := range ixns { + line := fmt.Sprintf("%s\x1f%s\x1f%s\x1f%s\x1f%d", + ixn.ID, ixn.SourceName, ixn.Action, ixn.DestinationName, ixn.Precedence) + result = append(result, line) + } + + output := columnize.Format(result, &columnize.Config{Delim: string([]byte{0x1f})}) + c.UI.Output(output) + + return 0 +} + +func (c *cmd) Synopsis() string { + return synopsis +} + +func (c *cmd) Help() string { + return c.help +} + +const synopsis = "List intentions." +const help = ` +Usage: consul intention list + + List all intentions. +` diff --git a/command/intention/list/intention_list_test.go b/command/intention/list/intention_list_test.go new file mode 100644 index 000000000..674efcc39 --- /dev/null +++ b/command/intention/list/intention_list_test.go @@ -0,0 +1,47 @@ +package list + +import ( + "strings" + "testing" + + "github.com/hashicorp/consul/agent" + "github.com/hashicorp/consul/api" + "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" +) + +func TestIntentionListCommand_noTabs(t *testing.T) { + t.Parallel() + if strings.ContainsRune(New(cli.NewMockUi()).Help(), '\t') { + t.Fatal("help has tabs") + } +} + +func TestIntentionListCommand(t *testing.T) { + t.Parallel() + require := require.New(t) + a := agent.NewTestAgent(t, ``) + defer a.Shutdown() + client := a.Client() + + // Create the intention + var id string + { + var err error + //nolint:staticcheck + id, _, err = client.Connect().IntentionCreate(&api.Intention{ + SourceName: "web", + DestinationName: "db", + Action: api.IntentionActionAllow, + }, nil) + require.NoError(err) + } + + // List all intentions + ui := cli.NewMockUi() + cmd := New(ui) + args := []string{"-http-addr=" + a.HTTPAddr()} + + require.Equal(0, cmd.Run(args), ui.ErrorWriter.String()) + require.Contains(ui.OutputWriter.String(), id) +} diff --git a/website/content/commands/intention/index.mdx b/website/content/commands/intention/index.mdx index 0c0073df2..898a21878 100644 --- a/website/content/commands/intention/index.mdx +++ b/website/content/commands/intention/index.mdx @@ -22,8 +22,8 @@ API](/api/connect/intentions). Usage: `consul intention ` -For the exact documentation for your Consul version, run `consul intention -h` to view -the complete list of subcommands. +For the exact documentation for your Consul version, run `consul intention -h` +to view the complete list of subcommands. ```text Usage: consul intention [options] [args] @@ -34,6 +34,7 @@ Subcommands: check Check whether a connection between two services is allowed. create Create intentions for service connections. delete Delete an intention. + list Lists all intentions. get Show information about an intention. match Show intentions that match a source or destination. ``` @@ -62,6 +63,12 @@ Test whether a "web" is allowed to connect to "db": $ consul intention check web db ``` +List all intentions: + +```shell-session +$ consul intention list +``` + Find all intentions for communicating to the "db" service: ```shell-session diff --git a/website/content/commands/intention/list.mdx b/website/content/commands/intention/list.mdx new file mode 100644 index 000000000..54168e2ee --- /dev/null +++ b/website/content/commands/intention/list.mdx @@ -0,0 +1,34 @@ +--- +layout: commands +page_title: 'Commands: Intention List' +sidebar_title: list +--- + +# Consul Intention List + +Command: `consul intention list` + +The `intention list` command shows all intentions including ID and precedence. + +## Usage + +Usage: + +- `consul intention list` + +#### API Options + +@include 'http_api_options_client.mdx' + +#### Enterprise Options + +@include 'http_api_namespace_options.mdx' + +## Examples + +```shell-session +$ consul intention list +ID Source Action Destination Precedence + web allow db 9 +36a6cf15-5f0e-a388-163e-0f608009704a dashboard allow counting 9 +```