From 8541244a5dfeb59f3abba02dc356345197c7db60 Mon Sep 17 00:00:00 2001 From: Michael Hofer Date: Tue, 12 Jan 2021 21:14:31 +0100 Subject: [PATCH] cli: Add consul intention list command (based on PR #6825) (#9468) This PR is based on the previous work by @snuggie12 in PR #6825. It adds the command consul intention list to list all available intentions. The list functionality for intentions seems a bit overdue as it's just very handy. The web UI cannot list intentions outside of the default namespace, and using the API is sometimes not the friendliest option. ;) I cherry picked snuggie12's commits who did most of the heavy lifting (thanks again @snuggie12 for your great work!). The changes in the original commit mostly still worked on the current HEAD. On top of that I added support for namespaces and fixed the docs as they are managed differently today. Also the requested changes related to the "Connect" references in the original PRs have been addressed. Fixes #5652 Co-authored-by: Matt Hoey --- .changelog/9468.txt | 3 + command/commands_oss.go | 2 + command/intention/intention.go | 4 + command/intention/list/intention_list.go | 85 +++++++++++++++++++ command/intention/list/intention_list_test.go | 47 ++++++++++ website/content/commands/intention/index.mdx | 11 ++- website/content/commands/intention/list.mdx | 34 ++++++++ 7 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 .changelog/9468.txt create mode 100644 command/intention/list/intention_list.go create mode 100644 command/intention/list/intention_list_test.go create mode 100644 website/content/commands/intention/list.mdx diff --git a/.changelog/9468.txt b/.changelog/9468.txt new file mode 100644 index 000000000000..80bfc591e5fc --- /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 d8e42f6424d8..781f74412459 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 94de26472291..44cc7db42deb 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 000000000000..929c99cb07a4 --- /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 000000000000..674efcc39ba8 --- /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 0c0073df29df..898a2187818e 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 000000000000..54168e2eef14 --- /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 +```