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