github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/dashboard_register.go (about) 1 package gateway 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "net/http" 10 "time" 11 12 "github.com/TykTechnologies/tyk/config" 13 "github.com/TykTechnologies/tyk/headers" 14 ) 15 16 var dashLog = log.WithField("prefix", "dashboard") 17 18 type NodeResponseOK struct { 19 Status string 20 Message map[string]string 21 Nonce string 22 } 23 24 type DashboardServiceSender interface { 25 Init() error 26 Register() error 27 DeRegister() error 28 StartBeating() error 29 StopBeating() 30 NotifyDashboardOfEvent(interface{}) error 31 } 32 33 type HTTPDashboardHandler struct { 34 RegistrationEndpoint string 35 DeRegistrationEndpoint string 36 HeartBeatEndpoint string 37 KeyQuotaTriggerEndpoint string 38 39 Secret string 40 41 heartBeatStopSentinel bool 42 } 43 44 var dashClient *http.Client 45 46 func initialiseClient() *http.Client { 47 if dashClient == nil { 48 dashClient = &http.Client{ 49 Timeout: 30 * time.Second, 50 } 51 52 if config.Global().HttpServerOptions.UseSSL { 53 // Setup HTTPS client 54 tlsConfig := &tls.Config{ 55 InsecureSkipVerify: config.Global().HttpServerOptions.SSLInsecureSkipVerify, 56 } 57 58 dashClient.Transport = &http.Transport{TLSClientConfig: tlsConfig} 59 } 60 } 61 62 return dashClient 63 } 64 65 func reLogin() { 66 if !config.Global().UseDBAppConfigs { 67 return 68 } 69 70 dashLog.Info("Registering node (again).") 71 DashService.StopBeating() 72 if err := DashService.DeRegister(); err != nil { 73 dashLog.Error("Could not deregister: ", err) 74 } 75 76 time.Sleep(5 * time.Second) 77 78 if err := DashService.Register(); err != nil { 79 dashLog.Error("Could not register: ", err) 80 } else { 81 go DashService.StartBeating() 82 } 83 84 dashLog.Info("Recovering configurations, reloading...") 85 reloadURLStructure(nil) 86 } 87 88 func (h *HTTPDashboardHandler) Init() error { 89 h.RegistrationEndpoint = buildConnStr("/register/node") 90 h.DeRegistrationEndpoint = buildConnStr("/system/node") 91 h.HeartBeatEndpoint = buildConnStr("/register/ping") 92 h.KeyQuotaTriggerEndpoint = buildConnStr("/system/key/quota_trigger") 93 94 if h.Secret = config.Global().NodeSecret; h.Secret == "" { 95 dashLog.Fatal("Node secret is not set, required for dashboard connection") 96 } 97 return nil 98 } 99 100 // NotifyDashboardOfEvent acts as a form of event which informs the 101 // dashboard of a key which has reached a certain usage quota 102 func (h *HTTPDashboardHandler) NotifyDashboardOfEvent(event interface{}) error { 103 104 meta, ok := event.(EventTriggerExceededMeta) 105 if !ok { 106 return errors.New("event type is currently not supported as a notification to the dashboard") 107 } 108 109 var b bytes.Buffer 110 if err := json.NewEncoder(&b).Encode(meta); err != nil { 111 log.Errorf("Could not decode event metadata :%v", err) 112 return err 113 } 114 115 req, err := http.NewRequest(http.MethodPost, h.KeyQuotaTriggerEndpoint, &b) 116 if err != nil { 117 log.Errorf("Could not create request.. %v", err) 118 return err 119 } 120 121 req.Header.Set("authorization", h.Secret) 122 req.Header.Set(headers.XTykNodeID, GetNodeID()) 123 req.Header.Set(headers.XTykNonce, ServiceNonce) 124 125 c := initialiseClient() 126 127 resp, err := c.Do(req) 128 if err != nil { 129 log.Errorf("Request failed with error %v", err) 130 return err 131 } 132 133 defer resp.Body.Close() 134 135 if resp.StatusCode != http.StatusOK { 136 err := fmt.Errorf("Unexpected status code while trying to notify dashboard of a key limit quota trigger.. Got %d", resp.StatusCode) 137 log.Error(err) 138 return err 139 } 140 141 val := NodeResponseOK{} 142 if err := json.NewDecoder(resp.Body).Decode(&val); err != nil { 143 return err 144 } 145 146 ServiceNonce = val.Nonce 147 148 return nil 149 } 150 151 func (h *HTTPDashboardHandler) Register() error { 152 dashLog.Info("Registering gateway node with Dashboard") 153 req := h.newRequest(h.RegistrationEndpoint) 154 c := initialiseClient() 155 resp, err := c.Do(req) 156 157 if err != nil { 158 dashLog.Errorf("Request failed with error %v; retrying in 5s", err) 159 time.Sleep(time.Second * 5) 160 return h.Register() 161 } else if resp != nil && resp.StatusCode != 200 { 162 dashLog.Errorf("Response failed with code %d; retrying in 5s", resp.StatusCode) 163 time.Sleep(time.Second * 5) 164 return h.Register() 165 } 166 167 defer resp.Body.Close() 168 val := NodeResponseOK{} 169 if err := json.NewDecoder(resp.Body).Decode(&val); err != nil { 170 return err 171 } 172 173 // Set the NodeID 174 var found bool 175 nodeID, found := val.Message["NodeID"] 176 SetNodeID(nodeID) 177 if !found { 178 dashLog.Error("Failed to register node, retrying in 5s") 179 time.Sleep(time.Second * 5) 180 return h.Register() 181 } 182 183 dashLog.WithField("id", GetNodeID()).Info("Node Registered") 184 185 // Set the nonce 186 ServiceNonce = val.Nonce 187 dashLog.Debug("Registration Finished: Nonce Set: ", ServiceNonce) 188 189 return nil 190 } 191 192 func (h *HTTPDashboardHandler) StartBeating() error { 193 194 req := h.newRequest(h.HeartBeatEndpoint) 195 196 client := initialiseClient() 197 198 for !h.heartBeatStopSentinel { 199 if err := h.sendHeartBeat(req, client); err != nil { 200 dashLog.Warning(err) 201 } 202 time.Sleep(time.Second * 2) 203 } 204 205 dashLog.Info("Stopped Heartbeat") 206 h.heartBeatStopSentinel = false 207 return nil 208 } 209 210 func (h *HTTPDashboardHandler) StopBeating() { 211 h.heartBeatStopSentinel = true 212 } 213 214 func (h *HTTPDashboardHandler) newRequest(endpoint string) *http.Request { 215 req, err := http.NewRequest("GET", endpoint, nil) 216 if err != nil { 217 panic(err) 218 } 219 req.Header.Set("authorization", h.Secret) 220 req.Header.Set(headers.XTykHostname, hostDetails.Hostname) 221 return req 222 } 223 224 func (h *HTTPDashboardHandler) sendHeartBeat(req *http.Request, client *http.Client) error { 225 req.Header.Set(headers.XTykNodeID, GetNodeID()) 226 req.Header.Set(headers.XTykNonce, ServiceNonce) 227 228 resp, err := client.Do(req) 229 if err != nil { 230 return errors.New("dashboard is down? Heartbeat is failing") 231 } 232 233 defer resp.Body.Close() 234 235 if resp.StatusCode == http.StatusForbidden { 236 return DashService.Register() 237 } 238 239 if resp.StatusCode != http.StatusOK { 240 return errors.New("dashboard is down? Heartbeat non-200 response") 241 } 242 val := NodeResponseOK{} 243 if err := json.NewDecoder(resp.Body).Decode(&val); err != nil { 244 return err 245 } 246 247 // Set the nonce 248 ServiceNonce = val.Nonce 249 //log.Debug("Heartbeat Finished: Nonce Set: ", ServiceNonce) 250 251 return nil 252 } 253 254 func (h *HTTPDashboardHandler) DeRegister() error { 255 req := h.newRequest(h.DeRegistrationEndpoint) 256 257 req.Header.Set(headers.XTykNodeID, GetNodeID()) 258 req.Header.Set(headers.XTykNonce, ServiceNonce) 259 260 c := initialiseClient() 261 resp, err := c.Do(req) 262 263 if err != nil { 264 return fmt.Errorf("deregister request failed with error %v", err) 265 } 266 defer resp.Body.Close() 267 if resp.StatusCode != http.StatusOK { 268 return fmt.Errorf("deregister request failed with status %v", resp.StatusCode) 269 } 270 271 val := NodeResponseOK{} 272 if err := json.NewDecoder(resp.Body).Decode(&val); err != nil { 273 return err 274 } 275 276 // Set the nonce 277 ServiceNonce = val.Nonce 278 dashLog.Info("De-registered.") 279 280 return nil 281 }