Skip to content

Commit b013b5d

Browse files
authored
Merge pull request #5 from myENA/feature/addr-update
Feature/addr update
2 parents cfcce4c + de88280 commit b013b5d

File tree

3 files changed

+110
-109
lines changed

3 files changed

+110
-109
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ Option | Description
2323
`-f` | Force killing of all matches, including healthy services
2424
`-s string` | Limit search by service address (regexp)
2525
`-t string` | Limit search by tag
26+
`-local-addr string` | Address with port of "local" agent to use to retrieve service list from (defaults to value of `CONSUL_HTTP_ADDR` env var)
27+
`-remote-port int` | Port to use when connecting to remote agents. Defaults to `8500`
28+
`-token string` | ACL token to use in all API requests
2629
`-v` | Verbose
2730
`-vv` | Increased verbosity
2831
`-vvv` | Super verbosity

zombie.go renamed to main.go

Lines changed: 107 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import (
66
"fmt"
77
"log"
88
"os"
9+
"regexp"
910

1011
"github.com/hashicorp/consul/api"
1112
"github.com/olekukonko/tablewriter"
1213
)
1314

1415
// this is the default port for talking to remote consul agents
15-
const defaultPort = 8500
16+
const (
17+
defaultPort = 8500
18+
)
1619

1720
type verbosityLevel uint8
1821

@@ -38,15 +41,16 @@ func usage(code int) {
3841
}
3942

4043
func main() {
41-
fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
44+
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
4245
serviceString := fs.String("s", "", "Limit search by service address (regexp)")
4346
tag := fs.String("t", "", "Limit search by tag")
4447
force := fs.Bool("f", false, "Force killing of all matches, including healthy services")
45-
port := fs.Int("port", defaultPort, "Port to use when connecting to remote agents")
46-
token := fs.String("token", "", "Token to use when connecting to remote agents")
48+
localAddr := fs.String("local-addr", os.Getenv(api.HTTPAddrEnvName), "Address with port of \"local\" agent. Used to list services.")
49+
remotePort := fs.Int("remote-port", defaultPort, "Port to use when connecting to remote agents")
50+
token := fs.String("token", os.Getenv(api.HTTPTokenEnvName), "ACL token. Used in all api queries.")
4751
v1 := fs.Bool("v", false, "Verbose")
48-
v2 := fs.Bool("vv", false, "Increased Verbosity")
49-
v3 := fs.Bool("vvv", false, "Super Verbosity")
52+
v2 := fs.Bool("vv", false, "Really verbose")
53+
v3 := fs.Bool("vvv", false, "Extremely verbose")
5054
if err := fs.Parse(os.Args[1:]); err != nil {
5155
log.Printf("Error parsing args: %s", err)
5256
os.Exit(1)
@@ -71,19 +75,111 @@ func main() {
7175
switch cmd {
7276
// define a couple synonyms to "hunt" as well
7377
case "hunt", "find", "search":
74-
serviceList := getList(*serviceString, *token, *tag)
78+
serviceList := getList(*localAddr, *token, *serviceString, *tag)
7579
printList(serviceList, verbosity)
7680

7781
case "kill":
78-
serviceList := getList(*serviceString, *token, *tag)
79-
deregister(serviceList, *port, *token, *force)
82+
serviceList := getList(*localAddr, *token, *serviceString, *tag)
83+
deregister(*remotePort, *token, serviceList, *force)
8084

8185
default:
8286
usage(1)
8387
}
8488

8589
}
8690

91+
// get a client handle for a specified address (or the local agent if "")
92+
func getClient(address, token string) (*api.Client, error) {
93+
config := api.DefaultConfig()
94+
if address != "" {
95+
config.Address = address
96+
}
97+
if token != "" {
98+
config.Token = token
99+
}
100+
return api.NewClient(config)
101+
}
102+
103+
// get a list of all services, limit to those matching the search criteria
104+
func getList(localAddr, token, serviceString, tag string) []*api.ServiceEntry {
105+
client, err := getClient(localAddr, token)
106+
if err != nil {
107+
log.Fatalf("Unable to get a consul client connection: %s\n", err)
108+
}
109+
110+
serviceList, _, err := client.Catalog().Services(nil)
111+
if err != nil {
112+
log.Fatalf("Unable to get list of services from catalog: %s", err)
113+
}
114+
115+
nodeServiceMap := make(map[string]map[string]*api.ServiceEntry)
116+
117+
for svc := range serviceList {
118+
entries, _, err := client.Health().Service(svc, tag, false, nil)
119+
if err != nil {
120+
log.Fatalf("Unable to query for service \"%s\" health: %s", svc, err)
121+
}
122+
for _, entry := range entries {
123+
if _, ok := nodeServiceMap[entry.Node.Node]; !ok {
124+
nodeServiceMap[entry.Node.Node] = make(map[string]*api.ServiceEntry)
125+
}
126+
nodeServiceMap[entry.Node.Node][entry.Service.ID] = entry
127+
}
128+
}
129+
130+
seOut := make([]*api.ServiceEntry, 0)
131+
if serviceString == "" {
132+
for _, services := range nodeServiceMap {
133+
for _, service := range services {
134+
seOut = append(seOut, service)
135+
}
136+
}
137+
} else {
138+
var (
139+
serviceID string
140+
services map[string]*api.ServiceEntry
141+
service *api.ServiceEntry
142+
)
143+
re := regexp.MustCompile(serviceString)
144+
for _, services = range nodeServiceMap {
145+
for serviceID, service = range services {
146+
if re.FindString(serviceID) != "" || re.FindString(service.Service.Service) != "" {
147+
seOut = append(seOut, service)
148+
}
149+
}
150+
}
151+
}
152+
153+
return seOut
154+
}
155+
156+
// A service entry is considered healty if all the eligible checks are passing.
157+
// serfChecks are not eligible
158+
func isHealthy(se *api.ServiceEntry) bool {
159+
if se == nil || se.Checks == nil || len(se.Checks) == 0 {
160+
// No checks = failing
161+
return false
162+
}
163+
164+
healthy := true
165+
eligible := 0
166+
for _, c := range se.Checks {
167+
if c.Name == "serfHealth" {
168+
continue
169+
}
170+
// All found checks have to be passing
171+
healthy = healthy && (c.Status == api.HealthPassing)
172+
eligible++
173+
}
174+
175+
// No eligible checks were found
176+
if eligible == 0 {
177+
return false
178+
}
179+
180+
return healthy
181+
}
182+
87183
// display a list of matching services
88184
func printList(serviceList []*api.ServiceEntry, v verbosityLevel) {
89185
var header, footer []string
@@ -142,10 +238,10 @@ func printList(serviceList []*api.ServiceEntry, v verbosityLevel) {
142238
}
143239

144240
// kill those services that are failing in the passed list, or all if force is true
145-
func deregister(serviceList []*api.ServiceEntry, port int, token string, force bool) {
241+
func deregister(remotePort int, token string, serviceList []*api.ServiceEntry, force bool) {
146242
for _, se := range serviceList {
147243
if !isHealthy(se) || force {
148-
fullAddress := fmt.Sprintf("%s:%d", se.Node.Address, port)
244+
fullAddress := fmt.Sprintf("%s:%d", se.Node.Address, remotePort)
149245
fmt.Printf("Deregistering %s: %s (%s)\n", se.Service.Service, se.Service.ID, fullAddress)
150246
client, err := getClient(fullAddress, token)
151247
if err != nil {

utilities.go

Lines changed: 0 additions & 98 deletions
This file was deleted.

0 commit comments

Comments
 (0)