diff --git a/command/commands_oss.go b/command/commands_oss.go index 6452a4f6f4..f166e82d72 100644 --- a/command/commands_oss.go +++ b/command/commands_oss.go @@ -17,6 +17,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" + ixnmatch "github.com/hashicorp/consul/command/intention/match" "github.com/hashicorp/consul/command/join" "github.com/hashicorp/consul/command/keygen" "github.com/hashicorp/consul/command/keyring" @@ -76,6 +77,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 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 }) Register("keyring", func(ui cli.Ui) (cli.Command, error) { return keyring.New(ui), nil }) diff --git a/command/intention/match/match.go b/command/intention/match/match.go new file mode 100644 index 0000000000..651ff8fefd --- /dev/null +++ b/command/intention/match/match.go @@ -0,0 +1,100 @@ +package match + +import ( + "flag" + "fmt" + "io" + + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/command/flags" + "github.com/mitchellh/cli" +) + +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 + + // flags + flagSource bool + flagDestination bool + + // testStdin is the input for testing. + testStdin io.Reader +} + +func (c *cmd) init() { + c.flags = flag.NewFlagSet("", flag.ContinueOnError) + c.flags.BoolVar(&c.flagSource, "source", false, + "Match intentions with the given source.") + c.flags.BoolVar(&c.flagDestination, "destination", false, + "Match intentions with the given destination.") + + c.http = &flags.HTTPFlags{} + flags.Merge(c.flags, c.http.ClientFlags()) + flags.Merge(c.flags, c.http.ServerFlags()) + c.help = flags.Usage(help, c.flags) +} + +func (c *cmd) Run(args []string) int { + if err := c.flags.Parse(args); err != nil { + return 2 + } + + args = c.flags.Args() + if len(args) != 1 { + c.UI.Error(fmt.Sprintf("Error: command requires exactly one argument: src or dst")) + return 2 + } + + // Create and test the HTTP client + client, err := c.http.APIClient() + if err != nil { + c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) + return 2 + } + + // Match the intention + matches, _, err := client.Connect().IntentionMatch(&api.IntentionMatch{ + By: api.IntentionMatchDestination, + Names: []string{args[0]}, + }, nil) + if err != nil { + c.UI.Error(fmt.Sprintf("Error matching the connection: %s", err)) + return 2 + } + + for _, ixn := range matches[args[0]] { + c.UI.Output(ixn.String()) + } + + return 0 +} + +func (c *cmd) Synopsis() string { + return synopsis +} + +func (c *cmd) Help() string { + return c.help +} + +const synopsis = "Show intentions that match a source or destination." +const help = ` +Usage: consul intention match [options] SRC|DST + + Show the list of intentions that would be enforced for a given source + or destination. The intentions are listed in the order they would be + evaluated. + + $ consul intention match db + $ consul intention match -source web + +`