github.com/sl1pm4t/consul@v1.4.5-0.20190325224627-74c31c540f9c/command/catalog/list/services/catalog_list_services.go (about)

     1  package services
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"fmt"
     7  	"sort"
     8  	"strings"
     9  	"text/tabwriter"
    10  
    11  	"github.com/hashicorp/consul/api"
    12  	"github.com/hashicorp/consul/command/flags"
    13  	"github.com/mitchellh/cli"
    14  )
    15  
    16  func New(ui cli.Ui) *cmd {
    17  	c := &cmd{UI: ui}
    18  	c.init()
    19  	return c
    20  }
    21  
    22  type cmd struct {
    23  	UI    cli.Ui
    24  	flags *flag.FlagSet
    25  	http  *flags.HTTPFlags
    26  	help  string
    27  
    28  	// flags
    29  	node     string
    30  	nodeMeta map[string]string
    31  	tags     bool
    32  }
    33  
    34  func (c *cmd) init() {
    35  	c.flags = flag.NewFlagSet("", flag.ContinueOnError)
    36  	c.flags.StringVar(&c.node, "node", "",
    37  		"Node `id or name` for which to list services.")
    38  	c.flags.Var((*flags.FlagMapValue)(&c.nodeMeta), "node-meta", "Metadata to "+
    39  		"filter nodes with the given `key=value` pairs. If specified, only "+
    40  		"services running on nodes matching the given metadata will be returned. "+
    41  		"This flag may be specified multiple times to filter on multiple sources "+
    42  		"of metadata.")
    43  	c.flags.BoolVar(&c.tags, "tags", false, "Display each service's tags as a "+
    44  		"comma-separated list beside each service entry.")
    45  
    46  	c.http = &flags.HTTPFlags{}
    47  	flags.Merge(c.flags, c.http.ClientFlags())
    48  	flags.Merge(c.flags, c.http.ServerFlags())
    49  	c.help = flags.Usage(help, c.flags)
    50  }
    51  
    52  func (c *cmd) Run(args []string) int {
    53  	if err := c.flags.Parse(args); err != nil {
    54  		return 1
    55  	}
    56  
    57  	if l := len(c.flags.Args()); l > 0 {
    58  		c.UI.Error(fmt.Sprintf("Too many arguments (expected 0, got %d)", l))
    59  		return 1
    60  	}
    61  
    62  	// Create and test the HTTP client
    63  	client, err := c.http.APIClient()
    64  	if err != nil {
    65  		c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
    66  		return 1
    67  	}
    68  
    69  	var services map[string][]string
    70  	if c.node != "" {
    71  		catalogNode, _, err := client.Catalog().Node(c.node, &api.QueryOptions{
    72  			NodeMeta: c.nodeMeta,
    73  		})
    74  		if err != nil {
    75  			c.UI.Error(fmt.Sprintf("Error listing services for node: %s", err))
    76  			return 1
    77  		}
    78  		if catalogNode != nil {
    79  			services = make(map[string][]string, len(catalogNode.Services))
    80  			for _, s := range catalogNode.Services {
    81  				services[s.Service] = append(services[s.Service], s.Tags...)
    82  			}
    83  		}
    84  	} else {
    85  		services, _, err = client.Catalog().Services(&api.QueryOptions{
    86  			NodeMeta: c.nodeMeta,
    87  		})
    88  		if err != nil {
    89  			c.UI.Error(fmt.Sprintf("Error listing services: %s", err))
    90  			return 1
    91  		}
    92  	}
    93  
    94  	// Handle the edge case where there are no services that match the query.
    95  	if len(services) == 0 {
    96  		c.UI.Error("No services match the given query - try expanding your search.")
    97  		return 0
    98  	}
    99  
   100  	// Order the map for consistent output
   101  	order := make([]string, 0, len(services))
   102  	for k := range services {
   103  		order = append(order, k)
   104  	}
   105  	sort.Strings(order)
   106  
   107  	if c.tags {
   108  		var b bytes.Buffer
   109  		tw := tabwriter.NewWriter(&b, 0, 2, 6, ' ', 0)
   110  		for _, s := range order {
   111  			sort.Strings(services[s])
   112  			fmt.Fprintf(tw, "%s\t%s\n", s, strings.Join(services[s], ","))
   113  		}
   114  		if err := tw.Flush(); err != nil {
   115  			c.UI.Error(fmt.Sprintf("Error flushing tabwriter: %s", err))
   116  			return 1
   117  		}
   118  		c.UI.Output(strings.TrimSpace(b.String()))
   119  	} else {
   120  		for _, s := range order {
   121  			c.UI.Output(s)
   122  		}
   123  	}
   124  
   125  	return 0
   126  }
   127  
   128  func (c *cmd) Synopsis() string {
   129  	return synopsis
   130  }
   131  
   132  func (c *cmd) Help() string {
   133  	return c.help
   134  }
   135  
   136  const synopsis = "Lists all registered services in a datacenter"
   137  const help = `
   138  Usage: consul catalog services [options]
   139  
   140    Retrieves the list services registered in a given datacenter. By default, the
   141    datacenter of the local agent is queried.
   142  
   143    To retrieve the list of services:
   144  
   145        $ consul catalog services
   146  
   147    To include the services' tags in the output:
   148  
   149        $ consul catalog services -tags
   150  
   151    To list services which run on a particular node:
   152  
   153        $ consul catalog services -node=web
   154  
   155    To filter services on node metadata:
   156  
   157        $ consul catalog services -node-meta="foo=bar"
   158  
   159    For a full list of options and examples, please see the Consul documentation.
   160  `