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 <mhoey05@jcu.edu>
This commit is contained in:
Michael Hofer 2021-01-12 21:14:31 +01:00 committed by GitHub
parent 5b92255cf5
commit acc843f04d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 184 additions and 2 deletions

3
.changelog/9468.txt Normal file
View File

@ -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.
```

View File

@ -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 })

View File

@ -40,6 +40,10 @@ Usage: consul intention <subcommand> [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

View File

@ -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.
`

View File

@ -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)
}

View File

@ -22,8 +22,8 @@ API](/api/connect/intentions).
Usage: `consul intention <subcommand>`
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 <subcommand> [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

View File

@ -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
```