Skip to content

Commit 989af94

Browse files
author
Flavio Crisciani
committed
Diagnostic client
- the client allows to talk to the diagnostic server and decode the internal values of the overlay and service discovery - the tool also allows to remediate in case of orphans entries Signed-off-by: Flavio Crisciani <[email protected]>
1 parent 0e28450 commit 989af94

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ build-local:
2828
@mkdir -p "bin"
2929
go build -tags experimental -o "bin/dnet" ./cmd/dnet
3030
go build -o "bin/docker-proxy" ./cmd/proxy
31+
GOOS=linux go build -o "./cmd/diagnostic/diagnosticClient" ./cmd/diagnostic
3132

3233
clean:
3334
@echo "🐳 $@"

cmd/diagnostic/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM docker:17.12-rc-dind
2+
3+
RUN apk add --no-cache curl
4+
5+
WORKDIR /tool
6+
7+
COPY daemon.json /etc/docker/daemon.json
8+
COPY diagnosticClient /tool/diagnosticClient

cmd/diagnostic/daemon.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"debug": true,
3+
"network-diagnostic-port": 2000
4+
}

cmd/diagnostic/main.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"encoding/base64"
6+
"encoding/json"
7+
"flag"
8+
"fmt"
9+
"io"
10+
"io/ioutil"
11+
"net/http"
12+
"os"
13+
"strings"
14+
15+
"github.com/docker/libnetwork"
16+
"github.com/docker/libnetwork/diagnose"
17+
"github.com/docker/libnetwork/drivers/overlay"
18+
"github.com/sirupsen/logrus"
19+
)
20+
21+
const (
22+
readyPath = "http://%s:%d/ready"
23+
joinNetwork = "http://%s:%d/joinnetwork?nid=%s"
24+
leaveNetwork = "http://%s:%d/leavenetwork?nid=%s"
25+
clusterPeers = "http://%s:%d/clusterpeers?json"
26+
networkPeers = "http://%s:%d/networkpeers?nid=%s&json"
27+
dumpTable = "http://%s:%d/gettable?nid=%s&tname=%s&json"
28+
deleteEntry = "http://%s:%d/deleteentry?nid=%s&tname=%s&key=%s&json"
29+
)
30+
31+
func httpIsOk(body io.ReadCloser) {
32+
b, err := ioutil.ReadAll(body)
33+
if err != nil {
34+
logrus.Fatalf("Failed the body parse %s", err)
35+
}
36+
if !strings.Contains(string(b), "OK") {
37+
logrus.Fatalf("Server not ready %s", b)
38+
}
39+
body.Close()
40+
}
41+
42+
func main() {
43+
ipPtr := flag.String("ip", "127.0.0.1", "ip address")
44+
portPtr := flag.Int("port", 2000, "port")
45+
networkPtr := flag.String("net", "", "target network")
46+
tablePtr := flag.String("t", "", "table to process <sd/overlay>")
47+
remediatePtr := flag.Bool("r", false, "perform remediation deleting orphan entries")
48+
verbosePtr := flag.Bool("v", false, "verbose output")
49+
50+
flag.Parse()
51+
52+
if *verbosePtr {
53+
logrus.SetLevel(logrus.DebugLevel)
54+
}
55+
56+
logrus.Infof("Connecting to %s:%d checking ready", *ipPtr, *portPtr)
57+
resp, err := http.Get(fmt.Sprintf(readyPath, *ipPtr, *portPtr))
58+
if err != nil {
59+
logrus.WithError(err).Fatalf("The connection failed")
60+
}
61+
httpIsOk(resp.Body)
62+
63+
clusterPeers := fetchNodePeers(*ipPtr, *portPtr, "")
64+
var networkPeers map[string]string
65+
var joinedNetwork bool
66+
if *networkPtr != "" {
67+
logrus.Infof("Joining the network:%s", *networkPtr)
68+
resp, err = http.Get(fmt.Sprintf(joinNetwork, *ipPtr, *portPtr, *networkPtr))
69+
if err != nil {
70+
logrus.WithError(err).Fatalf("Failed joining the network")
71+
}
72+
httpIsOk(resp.Body)
73+
networkPeers = fetchNodePeers(*ipPtr, *portPtr, *networkPtr)
74+
joinedNetwork = true
75+
}
76+
77+
switch *tablePtr {
78+
case "sd":
79+
fetchTable(*ipPtr, *portPtr, *networkPtr, "endpoint_table", clusterPeers, networkPeers, *remediatePtr)
80+
case "overlay":
81+
fetchTable(*ipPtr, *portPtr, *networkPtr, "overlay_peer_table", clusterPeers, networkPeers, *remediatePtr)
82+
}
83+
84+
if joinedNetwork {
85+
resp, err = http.Get(fmt.Sprintf(leaveNetwork, *ipPtr, *portPtr, *networkPtr))
86+
if err != nil {
87+
logrus.WithError(err).Fatalf("Failed leaving the network")
88+
}
89+
httpIsOk(resp.Body)
90+
}
91+
}
92+
93+
func fetchNodePeers(ip string, port int, network string) map[string]string {
94+
logrus.Infof("Fetch peers %s", network)
95+
var path string
96+
if network != "" {
97+
path = fmt.Sprintf(networkPeers, ip, port, network)
98+
} else {
99+
path = fmt.Sprintf(clusterPeers, ip, port)
100+
}
101+
102+
resp, err := http.Get(path)
103+
if err != nil {
104+
logrus.WithError(err).Fatalf("Failed fetching path")
105+
}
106+
defer resp.Body.Close()
107+
body, err := ioutil.ReadAll(resp.Body)
108+
if err != nil {
109+
logrus.WithError(err).Fatalf("Failed the body parse")
110+
}
111+
112+
output := diagnose.HTTPResult{Details: &diagnose.TablePeersResult{}}
113+
err = json.Unmarshal(body, &output)
114+
if err != nil {
115+
logrus.WithError(err).Fatalf("Failed the json unmarshalling")
116+
}
117+
118+
logrus.Debugf("Parsing JSON response")
119+
result := make(map[string]string, output.Details.(*diagnose.TablePeersResult).Length)
120+
for _, v := range output.Details.(*diagnose.TablePeersResult).Elements {
121+
logrus.Debugf("name:%s ip:%s", v.Name, v.IP)
122+
result[v.Name] = v.IP
123+
}
124+
return result
125+
}
126+
127+
func fetchTable(ip string, port int, network, tableName string, networkPeers, clusterPeers map[string]string, remediate bool) {
128+
logrus.Infof("Fetch %s table and check owners", tableName)
129+
resp, err := http.Get(fmt.Sprintf(dumpTable, ip, port, network, tableName))
130+
if err != nil {
131+
logrus.WithError(err).Fatalf("Failed fetching endpoint table")
132+
}
133+
defer resp.Body.Close()
134+
body, err := ioutil.ReadAll(resp.Body)
135+
if err != nil {
136+
logrus.WithError(err).Fatalf("Failed the body parse")
137+
}
138+
139+
output := diagnose.HTTPResult{Details: &diagnose.TableEndpointsResult{}}
140+
err = json.Unmarshal(body, &output)
141+
if err != nil {
142+
logrus.WithError(err).Fatalf("Failed the json unmarshalling")
143+
}
144+
145+
logrus.Debug("Parsing data structures")
146+
var orphanKeys []string
147+
for _, v := range output.Details.(*diagnose.TableEndpointsResult).Elements {
148+
decoded, err := base64.StdEncoding.DecodeString(v.Value)
149+
if err != nil {
150+
logrus.WithError(err).Errorf("Failed decoding entry")
151+
continue
152+
}
153+
switch tableName {
154+
case "endpoint_table":
155+
var elem libnetwork.EndpointRecord
156+
elem.Unmarshal(decoded)
157+
logrus.Debugf("key:%s value:%+v owner:%s", v.Key, elem, v.Owner)
158+
case "overlay_peer_table":
159+
var elem overlay.PeerRecord
160+
elem.Unmarshal(decoded)
161+
logrus.Debugf("key:%s value:%+v owner:%s", v.Key, elem, v.Owner)
162+
}
163+
164+
if _, ok := networkPeers[v.Owner]; !ok {
165+
logrus.Warnf("The element with key:%s does not belong to any node on this network", v.Key)
166+
orphanKeys = append(orphanKeys, v.Key)
167+
}
168+
if _, ok := clusterPeers[v.Owner]; !ok {
169+
logrus.Warnf("The element with key:%s does not belong to any node on this cluster", v.Key)
170+
}
171+
}
172+
173+
if len(orphanKeys) > 0 && remediate {
174+
logrus.Warnf("The following keys:%v results as orphan, do you want to proceed with the deletion (this operation is irreversible)? [Yes/No]", orphanKeys)
175+
reader := bufio.NewReader(os.Stdin)
176+
text, _ := reader.ReadString('\n')
177+
text = strings.Replace(text, "\n", "", -1)
178+
if strings.Compare(text, "Yes") == 0 {
179+
for _, k := range orphanKeys {
180+
resp, err := http.Get(fmt.Sprintf(deleteEntry, ip, port, network, tableName, k))
181+
if err != nil {
182+
logrus.WithError(err).Errorf("Failed deleting entry k:%s", k)
183+
break
184+
}
185+
resp.Body.Close()
186+
}
187+
} else {
188+
logrus.Infof("Deletion skipped")
189+
}
190+
}
191+
}

0 commit comments

Comments
 (0)