Browse Source

Merge pull request #1834 from gravitl/story/GRA-822

finished initial crud for hosts, fixed stun server close bug
Matthew R Kasun 2 years ago
parent
commit
a5e6aea563
6 changed files with 327 additions and 95 deletions
  1. 1 0
      controllers/controller.go
  2. 114 0
      controllers/hosts.go
  3. 68 90
      database/database.go
  4. 139 0
      logic/hosts.go
  5. 1 1
      models/host.go
  6. 4 4
      stun-server/stun-server.go

+ 1 - 0
controllers/controller.go

@@ -27,6 +27,7 @@ var HttpHandlers = []interface{}{
 	extClientHandlers,
 	ipHandlers,
 	loggerHandlers,
+	hostHandlers,
 }
 
 // HandleRESTRequests - handles the rest requests

+ 114 - 0
controllers/hosts.go

@@ -0,0 +1,114 @@
+package controller
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+)
+
+func hostHandlers(r *mux.Router) {
+	r.HandleFunc("/api/hosts", logic.SecurityCheck(false, http.HandlerFunc(getHosts))).Methods("GET")
+	r.HandleFunc("/api/hosts", logic.SecurityCheck(true, http.HandlerFunc(updateHost))).Methods("PUT")
+	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods("DELETE")
+	// r.HandleFunc("/api/hosts/{hostid}/{network}", logic.SecurityCheck(false, http.HandlerFunc(getHosts))).Methods("PUT")
+}
+
+// swagger:route GET /api/hosts hosts getHosts
+//
+// Lists all hosts.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: getHostsSliceResponse
+func getHosts(w http.ResponseWriter, r *http.Request) {
+	currentHosts, err := logic.GetAllHosts()
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to fetch hosts: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logger.Log(2, r.Header.Get("user"), "fetched all hosts")
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(currentHosts)
+}
+
+// swagger:route PUT /api/hosts/{hostid} hosts updateHost
+//
+// Updates a Netclient host on Netmaker server.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: updateHostResponse
+func updateHost(w http.ResponseWriter, r *http.Request) {
+	var newHostData models.Host
+	err := json.NewDecoder(r.Body).Decode(&newHostData)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	// confirm host exists
+	currHost, err := logic.GetHost(newHostData.ID.String())
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	logic.UpdateHost(&newHostData, currHost) // update the in memory struct values
+	if err = logic.UpsertHost(&newHostData); err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	logger.Log(2, r.Header.Get("user"), "updated host", newHostData.ID.String())
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(newHostData)
+}
+
+// swagger:route DELETE /api/hosts/{hostid} hosts deleteHost
+//
+// Deletes a Netclient host from Netmaker server.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: deleteHostResponse
+func deleteHost(w http.ResponseWriter, r *http.Request) {
+	var params = mux.Vars(r)
+	hostid := params["hostid"]
+	// confirm host exists
+	currHost, err := logic.GetHost(hostid)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	if err = logic.RemoveHost(currHost); err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	logger.Log(2, r.Header.Get("user"), "removed host", currHost.Name)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(currHost)
+}

+ 68 - 90
database/database.go

@@ -14,96 +14,73 @@ import (
 	"golang.org/x/crypto/nacl/box"
 )
 
-// NETWORKS_TABLE_NAME - networks table
-const NETWORKS_TABLE_NAME = "networks"
-
-// NODES_TABLE_NAME - nodes table
-const NODES_TABLE_NAME = "nodes"
-
-// DELETED_NODES_TABLE_NAME - deleted nodes table
-const DELETED_NODES_TABLE_NAME = "deletednodes"
-
-// USERS_TABLE_NAME - users table
-const USERS_TABLE_NAME = "users"
-
-// CERTS_TABLE_NAME - certificates table
-const CERTS_TABLE_NAME = "certs"
-
-// DNS_TABLE_NAME - dns table
-const DNS_TABLE_NAME = "dns"
-
-// EXT_CLIENT_TABLE_NAME - ext client table
-const EXT_CLIENT_TABLE_NAME = "extclients"
-
-// PEERS_TABLE_NAME - peers table
-const PEERS_TABLE_NAME = "peers"
-
-// SERVERCONF_TABLE_NAME - stores server conf
-const SERVERCONF_TABLE_NAME = "serverconf"
-
-// SERVER_UUID_TABLE_NAME - stores unique netmaker server data
-const SERVER_UUID_TABLE_NAME = "serveruuid"
-
-// SERVER_UUID_RECORD_KEY - telemetry thing
-const SERVER_UUID_RECORD_KEY = "serveruuid"
-
-// DATABASE_FILENAME - database file name
-const DATABASE_FILENAME = "netmaker.db"
-
-// GENERATED_TABLE_NAME - stores server generated k/v
-const GENERATED_TABLE_NAME = "generated"
-
-// NODE_ACLS_TABLE_NAME - stores the node ACL rules
-const NODE_ACLS_TABLE_NAME = "nodeacls"
-
-// SSO_STATE_CACHE - holds sso session information for OAuth2 sign-ins
-const SSO_STATE_CACHE = "ssostatecache"
-
-// METRICS_TABLE_NAME - stores network metrics
-const METRICS_TABLE_NAME = "metrics"
-
-// NETWORK_USER_TABLE_NAME - network user table tracks stats for a network user per network
-const NETWORK_USER_TABLE_NAME = "networkusers"
-
-// USER_GROUPS_TABLE_NAME - table for storing usergroups
-const USER_GROUPS_TABLE_NAME = "usergroups"
-
-// CACHE_TABLE_NAME - caching table
-const CACHE_TABLE_NAME = "cache"
-
-// == ERROR CONSTS ==
-
-// NO_RECORD - no singular result found
-const NO_RECORD = "no result found"
-
-// NO_RECORDS - no results found
-const NO_RECORDS = "could not find any records"
-
-// == Constants ==
-
-// INIT_DB - initialize db
-const INIT_DB = "init"
-
-// CREATE_TABLE - create table const
-const CREATE_TABLE = "createtable"
-
-// INSERT - insert into db const
-const INSERT = "insert"
-
-// INSERT_PEER - insert peer into db const
-const INSERT_PEER = "insertpeer"
-
-// DELETE - delete db record const
-const DELETE = "delete"
-
-// DELETE_ALL - delete a table const
-const DELETE_ALL = "deleteall"
-
-// FETCH_ALL - fetch table contents const
-const FETCH_ALL = "fetchall"
-
-// CLOSE_DB - graceful close of db const
-const CLOSE_DB = "closedb"
+const (
+	// == Table Names ==
+	// NETWORKS_TABLE_NAME - networks table
+	NETWORKS_TABLE_NAME = "networks"
+	// NODES_TABLE_NAME - nodes table
+	NODES_TABLE_NAME = "nodes"
+	// DELETED_NODES_TABLE_NAME - deleted nodes table
+	DELETED_NODES_TABLE_NAME = "deletednodes"
+	// USERS_TABLE_NAME - users table
+	USERS_TABLE_NAME = "users"
+	// CERTS_TABLE_NAME - certificates table
+	CERTS_TABLE_NAME = "certs"
+	// DNS_TABLE_NAME - dns table
+	DNS_TABLE_NAME = "dns"
+	// EXT_CLIENT_TABLE_NAME - ext client table
+	EXT_CLIENT_TABLE_NAME = "extclients"
+	// PEERS_TABLE_NAME - peers table
+	PEERS_TABLE_NAME = "peers"
+	// SERVERCONF_TABLE_NAME - stores server conf
+	SERVERCONF_TABLE_NAME = "serverconf"
+	// SERVER_UUID_TABLE_NAME - stores unique netmaker server data
+	SERVER_UUID_TABLE_NAME = "serveruuid"
+	// SERVER_UUID_RECORD_KEY - telemetry thing
+	SERVER_UUID_RECORD_KEY = "serveruuid"
+	// DATABASE_FILENAME - database file name
+	DATABASE_FILENAME = "netmaker.db"
+	// GENERATED_TABLE_NAME - stores server generated k/v
+	GENERATED_TABLE_NAME = "generated"
+	// NODE_ACLS_TABLE_NAME - stores the node ACL rules
+	NODE_ACLS_TABLE_NAME = "nodeacls"
+	// SSO_STATE_CACHE - holds sso session information for OAuth2 sign-ins
+	SSO_STATE_CACHE = "ssostatecache"
+	// METRICS_TABLE_NAME - stores network metrics
+	METRICS_TABLE_NAME = "metrics"
+	// NETWORK_USER_TABLE_NAME - network user table tracks stats for a network user per network
+	NETWORK_USER_TABLE_NAME = "networkusers"
+	// USER_GROUPS_TABLE_NAME - table for storing usergroups
+	USER_GROUPS_TABLE_NAME = "usergroups"
+	// CACHE_TABLE_NAME - caching table
+	CACHE_TABLE_NAME = "cache"
+	// HOSTS_TABLE_NAME - the table name for hosts
+	HOSTS_TABLE_NAME = "hosts"
+
+	// == ERROR CONSTS ==
+	// NO_RECORD - no singular result found
+	NO_RECORD = "no result found"
+	// NO_RECORDS - no results found
+	NO_RECORDS = "could not find any records"
+
+	// == DB Constants ==
+	// INIT_DB - initialize db
+	INIT_DB = "init"
+	// CREATE_TABLE - create table const
+	CREATE_TABLE = "createtable"
+	// INSERT - insert into db const
+	INSERT = "insert"
+	// INSERT_PEER - insert peer into db const
+	INSERT_PEER = "insertpeer"
+	// DELETE - delete db record const
+	DELETE = "delete"
+	// DELETE_ALL - delete a table const
+	DELETE_ALL = "deleteall"
+	// FETCH_ALL - fetch table contents const
+	FETCH_ALL = "fetchall"
+	// CLOSE_DB - graceful close of db const
+	CLOSE_DB = "closedb"
+)
 
 func getCurrentDB() map[string]interface{} {
 	switch servercfg.GetDB() {
@@ -155,6 +132,7 @@ func createTables() {
 	createTable(NETWORK_USER_TABLE_NAME)
 	createTable(USER_GROUPS_TABLE_NAME)
 	createTable(CACHE_TABLE_NAME)
+	createTable(HOSTS_TABLE_NAME)
 }
 
 func createTable(tableName string) error {

+ 139 - 0
logic/hosts.go

@@ -0,0 +1,139 @@
+package logic
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/models"
+)
+
+// GetAllHosts - returns all hosts in flat list or error
+func GetAllHosts() ([]models.Host, error) {
+	currHostMap, err := GetHostsMap()
+	if err != nil {
+		return nil, err
+	}
+	var currentHosts = []models.Host{}
+	for k := range currHostMap {
+		var h = *currHostMap[k]
+		currentHosts = append(currentHosts, h)
+	}
+
+	return currentHosts, nil
+}
+
+// GetHostsMap - gets all the current hosts on machine in a map
+func GetHostsMap() (map[string]*models.Host, error) {
+	records, err := database.FetchRecords(database.HOSTS_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return nil, err
+	}
+	currHostMap := make(map[string]*models.Host)
+	for k := range records {
+		var h models.Host
+		err = json.Unmarshal([]byte(records[k]), &h)
+		if err != nil {
+			return nil, err
+		}
+		currHostMap[h.ID.String()] = &h
+	}
+
+	return currHostMap, nil
+}
+
+// GetHost - gets a host from db given id
+func GetHost(hostid string) (*models.Host, error) {
+	record, err := database.FetchRecord(database.HOSTS_TABLE_NAME, hostid)
+	if err != nil {
+		return nil, err
+	}
+
+	var h models.Host
+	if err = json.Unmarshal([]byte(record), &h); err != nil {
+		return nil, err
+	}
+
+	return &h, nil
+}
+
+// CreateHost - creates a host if not exist
+func CreateHost(h *models.Host) error {
+	_, err := GetHost(h.ID.String())
+	if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
+		return fmt.Errorf("host already exists")
+	}
+
+	return UpsertHost(h)
+}
+
+// UpdateHost - updates host data by field
+func UpdateHost(newHost, currentHost *models.Host) {
+	// unchangeable fields via API here
+	newHost.DaemonInstalled = currentHost.DaemonInstalled
+	newHost.OS = currentHost.OS
+	newHost.IPForwarding = currentHost.IPForwarding
+	newHost.HostPass = currentHost.HostPass
+	newHost.NodePassword = currentHost.NodePassword
+	newHost.MacAddress = currentHost.MacAddress
+	newHost.Debug = currentHost.Debug
+	newHost.Nodes = currentHost.Nodes
+	newHost.PublicKey = currentHost.PublicKey
+	newHost.InternetGateway = currentHost.InternetGateway
+	newHost.TrafficKeyPublic = currentHost.TrafficKeyPublic
+
+	// changeable fields
+	if len(newHost.Version) == 0 {
+		newHost.Version = currentHost.Version
+	}
+
+	if len(newHost.Name) == 0 {
+		newHost.Name = currentHost.Name
+	}
+
+	if newHost.LocalAddress.String() != currentHost.LocalAddress.String() {
+		newHost.LocalAddress = currentHost.LocalAddress
+	}
+
+	if newHost.LocalRange.String() != currentHost.LocalRange.String() {
+		newHost.LocalRange = currentHost.LocalRange
+	}
+
+	if newHost.MTU == 0 {
+		newHost.MTU = currentHost.MTU
+	}
+
+	if newHost.ListenPort == 0 {
+		newHost.ListenPort = currentHost.ListenPort
+	}
+
+	if newHost.ProxyListenPort == 0 {
+		newHost.ProxyListenPort = currentHost.ProxyListenPort
+	}
+}
+
+// UpsertHost - upserts into DB a given host model, does not check for existence*
+func UpsertHost(h *models.Host) error {
+	data, err := json.Marshal(h)
+	if err != nil {
+		return err
+	}
+
+	return database.Insert(h.ID.String(), string(data), database.HOSTS_TABLE_NAME)
+}
+
+// RemoveHost - removes a given host from server
+func RemoveHost(h *models.Host) error {
+	if len(h.Nodes) > 0 {
+		for i := range h.Nodes {
+			id := h.Nodes[i]
+			n, err := GetNodeByID(id)
+			if err == nil {
+				if err = DeleteNodeByID(&n); err != nil {
+					return err // must remove associated nodes before removing a host
+				}
+			}
+		}
+	}
+	return database.DeleteRecord(database.HOSTS_TABLE_NAME, h.ID.String())
+}

+ 1 - 1
models/host.go

@@ -30,5 +30,5 @@ type Host struct {
 	MacAddress       net.HardwareAddr `json:"macaddress" yaml:"macaddress"`
 	TrafficKeyPublic []byte           `json:"traffickeypublic" yaml:"trafficekeypublic"`
 	InternetGateway  net.UDPAddr      `json:"internetgateway" yaml:"internetgateway"`
-	Nodes            []Node           `json:"nodes" yaml:"nodes"`
+	Nodes            []string         `json:"nodes" yaml:"nodes"`
 }

+ 4 - 4
stun-server/stun-server.go

@@ -144,20 +144,20 @@ func normalize(address string) string {
 	return address
 }
 
+// Start - starts the stun server
 func Start(wg *sync.WaitGroup) {
-	defer wg.Done()
 	ctx, cancel := context.WithCancel(context.Background())
-	go func() {
+	go func(wg *sync.WaitGroup) {
+		defer wg.Done()
 		quit := make(chan os.Signal, 1)
 		signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
 		<-quit
 		cancel()
-	}()
+	}(wg)
 	normalized := normalize(fmt.Sprintf("0.0.0.0:%d", servercfg.GetStunPort()))
 	logger.Log(0, "netmaker-stun listening on", normalized, "via udp")
 	err := listenUDPAndServe(ctx, "udp", normalized)
 	if err != nil {
 		logger.Log(0, "failed to start stun server: ", err.Error())
 	}
-
 }