github.com/hernad/nomad@v1.6.112/command/service_list.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package command 5 6 import ( 7 "fmt" 8 "sort" 9 "strings" 10 11 "github.com/hernad/nomad/api" 12 "github.com/mitchellh/cli" 13 "github.com/posener/complete" 14 ) 15 16 // Ensure ServiceListCommand satisfies the cli.Command interface. 17 var _ cli.Command = &ServiceListCommand{} 18 19 // ServiceListCommand implements cli.Command. 20 type ServiceListCommand struct { 21 Meta 22 } 23 24 // Help satisfies the cli.Command Help function. 25 func (s *ServiceListCommand) Help() string { 26 helpText := ` 27 Usage: nomad service list [options] 28 29 List is used to list the currently registered services. 30 31 If ACLs are enabled, this command requires a token with the 'read-job' 32 capabilities for the namespace of all services. Any namespaces that the token 33 does not have access to will have its services filtered from the results. 34 35 General Options: 36 37 ` + generalOptionsUsage(usageOptsDefault) + ` 38 39 Service List Options: 40 41 -json 42 Output the services in JSON format. 43 44 -t 45 Format and display the services using a Go template. 46 ` 47 return strings.TrimSpace(helpText) 48 } 49 50 // Synopsis satisfies the cli.Command Synopsis function. 51 func (s *ServiceListCommand) Synopsis() string { 52 return "Display all registered Nomad services" 53 } 54 55 func (s *ServiceListCommand) AutocompleteFlags() complete.Flags { 56 return mergeAutocompleteFlags(s.Meta.AutocompleteFlags(FlagSetClient), 57 complete.Flags{ 58 "-json": complete.PredictNothing, 59 "-t": complete.PredictAnything, 60 }) 61 } 62 63 // Name returns the name of this command. 64 func (s *ServiceListCommand) Name() string { return "service list" } 65 66 // Run satisfies the cli.Command Run function. 67 func (s *ServiceListCommand) Run(args []string) int { 68 69 var ( 70 json bool 71 tmpl, name string 72 ) 73 74 flags := s.Meta.FlagSet(s.Name(), FlagSetClient) 75 flags.Usage = func() { s.Ui.Output(s.Help()) } 76 flags.BoolVar(&json, "json", false, "") 77 flags.StringVar(&name, "name", "", "") 78 flags.StringVar(&tmpl, "t", "", "") 79 if err := flags.Parse(args); err != nil { 80 return 1 81 } 82 83 if args = flags.Args(); len(args) > 0 { 84 s.Ui.Error("This command takes no arguments") 85 s.Ui.Error(commandErrorText(s)) 86 return 1 87 } 88 89 client, err := s.Meta.Client() 90 if err != nil { 91 s.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 92 return 1 93 } 94 95 list, _, err := client.Services().List(nil) 96 if err != nil { 97 s.Ui.Error(fmt.Sprintf("Error listing service registrations: %s", err)) 98 return 1 99 } 100 101 if len(list) == 0 { 102 s.Ui.Output("No service registrations found") 103 return 0 104 } 105 106 if json || len(tmpl) > 0 { 107 out, err := Format(json, tmpl, list) 108 if err != nil { 109 s.Ui.Error(err.Error()) 110 return 1 111 } 112 s.Ui.Output(out) 113 return 0 114 } 115 116 s.formatOutput(list) 117 return 0 118 } 119 120 func (s *ServiceListCommand) formatOutput(regs []*api.ServiceRegistrationListStub) { 121 122 // Create objects to hold sorted a sorted namespace array and a mapping, so 123 // we can perform service lookups on a namespace basis. 124 sortedNamespaces := make([]string, len(regs)) 125 namespacedServices := make(map[string][]*api.ServiceRegistrationStub) 126 127 for i, namespaceServices := range regs { 128 sortedNamespaces[i] = namespaceServices.Namespace 129 namespacedServices[namespaceServices.Namespace] = namespaceServices.Services 130 } 131 132 // Sort the namespaces. 133 sort.Strings(sortedNamespaces) 134 135 // The table always starts with the service name. 136 outputTable := []string{"Service Name"} 137 138 // If the request was made using the wildcard namespace, include this in 139 // the output. 140 if s.Meta.namespace == api.AllNamespacesNamespace { 141 outputTable[0] += "|Namespace" 142 } 143 144 // The tags come last and are always present. 145 outputTable[0] += "|Tags" 146 147 for _, ns := range sortedNamespaces { 148 149 // Grab the services belonging to this namespace. 150 services := namespacedServices[ns] 151 152 // Create objects to hold sorted a sorted service name array and a 153 // mapping, so we can perform service tag lookups on a name basis. 154 sortedNames := make([]string, len(services)) 155 serviceTags := make(map[string][]string) 156 157 for i, service := range services { 158 sortedNames[i] = service.ServiceName 159 serviceTags[service.ServiceName] = service.Tags 160 } 161 162 // Sort the service names. 163 sort.Strings(sortedNames) 164 165 for _, serviceName := range sortedNames { 166 167 // Grab the service tags, and sort these for good measure. 168 tags := serviceTags[serviceName] 169 sort.Strings(tags) 170 171 // Build the output array entry. 172 regOutput := serviceName 173 174 if s.Meta.namespace == api.AllNamespacesNamespace { 175 regOutput += "|" + ns 176 } 177 regOutput += "|" + fmt.Sprintf("[%s]", strings.Join(tags, ",")) 178 outputTable = append(outputTable, regOutput) 179 } 180 } 181 182 s.Ui.Output(formatList(outputTable)) 183 }