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  }