-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Exported services CLI and docs (#20331)
* Exported services CLI and docs * Changelog added * Added format option for pretty print * Update command/exportedservices/exported_services.go Co-authored-by: Ashesh Vidyut <134911583+absolutelightning@users.noreply.github.com> * Addressing PR comments, moving the command under services category * Add consumer peer and partition filter * Adding bexpr filter, change format of data --------- Co-authored-by: Ashesh Vidyut <134911583+absolutelightning@users.noreply.github.com>
- Loading branch information
1 parent
0ad0d40
commit bc310e4
Showing
8 changed files
with
712 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:feature | ||
cli: Adds new command `exported-services` to list all services exported and their consumers. Refer to the [CLI docs](https://developer.hashicorp.com/consul/commands/exported-services) for more information. | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package exportedservices | ||
|
||
import ( | ||
"encoding/json" | ||
"flag" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/mitchellh/cli" | ||
|
||
"github.com/hashicorp/consul/api" | ||
"github.com/hashicorp/consul/command/flags" | ||
"github.com/hashicorp/go-bexpr" | ||
"github.com/ryanuber/columnize" | ||
) | ||
|
||
const ( | ||
PrettyFormat string = "pretty" | ||
JSONFormat string = "json" | ||
) | ||
|
||
func getSupportedFormats() []string { | ||
return []string{PrettyFormat, JSONFormat} | ||
} | ||
|
||
func formatIsValid(f string) bool { | ||
for _, format := range getSupportedFormats() { | ||
if f == format { | ||
return true | ||
} | ||
} | ||
return false | ||
|
||
} | ||
|
||
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 | ||
|
||
format string | ||
filter string | ||
} | ||
|
||
func (c *cmd) init() { | ||
c.flags = flag.NewFlagSet("", flag.ContinueOnError) | ||
|
||
c.flags.StringVar( | ||
&c.format, | ||
"format", | ||
PrettyFormat, | ||
fmt.Sprintf("Output format {%s} (default: %s)", strings.Join(getSupportedFormats(), "|"), PrettyFormat), | ||
) | ||
|
||
c.flags.StringVar(&c.filter, "filter", "", "go-bexpr filter string to filter the response") | ||
|
||
c.http = &flags.HTTPFlags{} | ||
flags.Merge(c.flags, c.http.ClientFlags()) | ||
flags.Merge(c.flags, c.http.ServerFlags()) | ||
flags.Merge(c.flags, c.http.PartitionFlag()) | ||
c.help = flags.Usage(help, c.flags) | ||
} | ||
|
||
func (c *cmd) Run(args []string) int { | ||
if err := c.flags.Parse(args); err != nil { | ||
return 1 | ||
} | ||
|
||
if !formatIsValid(c.format) { | ||
c.UI.Error(fmt.Sprintf("Invalid format, valid formats are {%s}", strings.Join(getSupportedFormats(), "|"))) | ||
return 1 | ||
} | ||
|
||
client, err := c.http.APIClient() | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error connect to Consul agent: %s", err)) | ||
return 1 | ||
} | ||
|
||
exportedServices, _, err := client.ExportedServices(nil) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error reading exported services: %v", err)) | ||
return 1 | ||
} | ||
|
||
var filterType []api.ResolvedExportedService | ||
filter, err := bexpr.CreateFilter(c.filter, nil, filterType) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error while creating filter: %s", err)) | ||
return 1 | ||
} | ||
|
||
raw, err := filter.Execute(exportedServices) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error while filtering response: %s", err)) | ||
return 1 | ||
} | ||
|
||
filteredServices := raw.([]api.ResolvedExportedService) | ||
|
||
if len(filteredServices) == 0 { | ||
c.UI.Info("No exported services found") | ||
return 0 | ||
} | ||
|
||
if c.format == JSONFormat { | ||
output, err := json.MarshalIndent(filteredServices, "", " ") | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error marshalling JSON: %s", err)) | ||
return 1 | ||
} | ||
c.UI.Output(string(output)) | ||
return 0 | ||
} | ||
|
||
c.UI.Output(formatExportedServices(filteredServices)) | ||
|
||
return 0 | ||
} | ||
|
||
func formatExportedServices(services []api.ResolvedExportedService) string { | ||
result := make([]string, 0, len(services)+1) | ||
|
||
if services[0].Partition != "" { | ||
result = append(result, "Service\x1fPartition\x1fNamespace\x1fConsumer Peers\x1fConsumer Partitions") | ||
} else { | ||
result = append(result, "Service\x1fConsumer Peers") | ||
} | ||
|
||
for _, expService := range services { | ||
row := "" | ||
peers := strings.Join(expService.Consumers.Peers, ", ") | ||
partitions := strings.Join(expService.Consumers.Partitions, ", ") | ||
if expService.Partition != "" { | ||
row = fmt.Sprintf("%s\x1f%s\x1f%s\x1f%s\x1f%s", expService.Service, expService.Partition, expService.Namespace, peers, partitions) | ||
} else { | ||
row = fmt.Sprintf("%s\x1f%s", expService.Service, peers) | ||
} | ||
|
||
result = append(result, row) | ||
|
||
} | ||
|
||
return columnize.Format(result, &columnize.Config{Delim: string([]byte{0x1f})}) | ||
} | ||
|
||
func (c *cmd) Synopsis() string { | ||
return synopsis | ||
} | ||
|
||
func (c *cmd) Help() string { | ||
return flags.Usage(c.help, nil) | ||
} | ||
|
||
const ( | ||
synopsis = "Lists exported services" | ||
help = ` | ||
Usage: consul services exported-services [options] | ||
Lists all the exported services and their consumers. Wildcards and sameness groups(Enterprise) are expanded. | ||
Example: | ||
$ consul services exported-services | ||
` | ||
) |
Oops, something went wrong.