Browse Source

[NET-494 / ACC-322] New free tier limits (#2495)

* Rename var

* Rename consts and use iota

* Use switch instead of repeated else if

* Rename limits related vars

* Introduce new free tier limits

* Measure new limits and report on license validation

* Separate usage and limits, have new ones

* Don't check for hosts and clients limits, but for machines instead

* Error on egress creation @ free tier w/ internet gateways

* Remove clients and hosts limit from code

* Rename var

* Rename consts and use iota

* Use switch instead of repeated else if

* Rename limits related vars

* Introduce new free tier limits

* Measure new limits and report on license validation

* Separate usage and limits, have new ones

* Don't check for hosts and clients limits, but for machines instead

* Error on egress creation @ free tier w/ internet gateways

* Remove clients and hosts limit from code
Gabriel de Souza Seibel 2 years ago
parent
commit
8ce7da2ce9

+ 3 - 2
config/config.go

@@ -82,9 +82,10 @@ type ServerConfig struct {
 	TurnPassword               string `yaml:"turn_password"`
 	UseTurn                    bool   `yaml:"use_turn"`
 	UsersLimit                 int    `yaml:"user_limit"`
-	ClientsLimit               int    `yaml:"client_limit"`
 	NetworksLimit              int    `yaml:"network_limit"`
-	HostsLimit                 int    `yaml:"host_limit"`
+	MachinesLimit              int    `yaml:"machines_limit"`
+	IngressesLimit             int    `yaml:"ingresses_limit"`
+	EgressesLimit              int    `yaml:"egresses_limit"`
 	DeployedByOperator         bool   `yaml:"deployed_by_operator"`
 	Environment                string `yaml:"environment"`
 }

+ 1 - 1
controllers/ext_client.go

@@ -29,7 +29,7 @@ func extClientHandlers(r *mux.Router) {
 	r.HandleFunc("/api/extclients/{network}/{clientid}/{type}", logic.NetUserSecurityCheck(false, true, http.HandlerFunc(getExtClientConf))).Methods(http.MethodGet)
 	r.HandleFunc("/api/extclients/{network}/{clientid}", logic.NetUserSecurityCheck(false, true, http.HandlerFunc(updateExtClient))).Methods(http.MethodPut)
 	r.HandleFunc("/api/extclients/{network}/{clientid}", logic.NetUserSecurityCheck(false, true, http.HandlerFunc(deleteExtClient))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.NetUserSecurityCheck(false, true, checkFreeTierLimits(clients_l, http.HandlerFunc(createExtClient)))).Methods(http.MethodPost)
+	r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.NetUserSecurityCheck(false, true, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))).Methods(http.MethodPost)
 }
 
 func checkIngressExists(nodeID string) bool {

+ 40 - 16
controllers/limits.go

@@ -10,36 +10,60 @@ import (
 
 // limit consts
 const (
-	node_l     = 0
-	networks_l = 1
-	users_l    = 2
-	clients_l  = 3
+	limitChoiceNetworks = iota
+	limitChoiceUsers
+	limitChoiceMachines
+	limitChoiceIngress
+	limitChoiceEgress
 )
 
-func checkFreeTierLimits(limit_choice int, next http.Handler) http.HandlerFunc {
+func checkFreeTierLimits(limitChoice int, next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{
-			Code: http.StatusForbidden, Message: "free tier limits exceeded on networks",
+			Code: http.StatusForbidden, Message: "free tier limits exceeded on ",
 		}
 
-		if logic.Free_Tier { // check that free tier limits not exceeded
-			if limit_choice == networks_l {
+		if logic.FreeTier { // check that free tier limits not exceeded
+			switch limitChoice {
+			case limitChoiceNetworks:
 				currentNetworks, err := logic.GetNetworks()
-				if (err != nil && !database.IsEmptyRecord(err)) || len(currentNetworks) >= logic.Networks_Limit {
+				if (err != nil && !database.IsEmptyRecord(err)) ||
+					len(currentNetworks) >= logic.NetworksLimit {
+					errorResponse.Message += "networks"
 					logic.ReturnErrorResponse(w, r, errorResponse)
 					return
 				}
-			} else if limit_choice == users_l {
+			case limitChoiceUsers:
 				users, err := logic.GetUsers()
-				if (err != nil && !database.IsEmptyRecord(err)) || len(users) >= logic.Users_Limit {
-					errorResponse.Message = "free tier limits exceeded on users"
+				if (err != nil && !database.IsEmptyRecord(err)) ||
+					len(users) >= logic.UsersLimit {
+					errorResponse.Message += "users"
 					logic.ReturnErrorResponse(w, r, errorResponse)
 					return
 				}
-			} else if limit_choice == clients_l {
-				clients, err := logic.GetAllExtClients()
-				if (err != nil && !database.IsEmptyRecord(err)) || len(clients) >= logic.Clients_Limit {
-					errorResponse.Message = "free tier limits exceeded on external clients"
+			case limitChoiceMachines:
+				hosts, hErr := logic.GetAllHosts()
+				clients, cErr := logic.GetAllExtClients()
+				if (hErr != nil && !database.IsEmptyRecord(hErr)) ||
+					(cErr != nil && !database.IsEmptyRecord(cErr)) ||
+					len(hosts)+len(clients) >= logic.MachinesLimit {
+					errorResponse.Message += "machines"
+					logic.ReturnErrorResponse(w, r, errorResponse)
+					return
+				}
+			case limitChoiceIngress:
+				ingresses, err := logic.GetAllIngresses()
+				if (err != nil && !database.IsEmptyRecord(err)) ||
+					len(ingresses) >= logic.IngressesLimit {
+					errorResponse.Message += "ingresses"
+					logic.ReturnErrorResponse(w, r, errorResponse)
+					return
+				}
+			case limitChoiceEgress:
+				egresses, err := logic.GetAllEgresses()
+				if (err != nil && !database.IsEmptyRecord(err)) ||
+					len(egresses) >= logic.EgressesLimit {
+					errorResponse.Message += "egresses"
 					logic.ReturnErrorResponse(w, r, errorResponse)
 					return
 				}

+ 1 - 1
controllers/network.go

@@ -21,7 +21,7 @@ import (
 
 func networkHandlers(r *mux.Router) {
 	r.HandleFunc("/api/networks", logic.SecurityCheck(false, http.HandlerFunc(getNetworks))).Methods(http.MethodGet)
-	r.HandleFunc("/api/networks", logic.SecurityCheck(true, checkFreeTierLimits(networks_l, http.HandlerFunc(createNetwork)))).Methods(http.MethodPost)
+	r.HandleFunc("/api/networks", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceNetworks, http.HandlerFunc(createNetwork)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(false, http.HandlerFunc(getNetwork))).Methods(http.MethodGet)
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetwork))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(updateNetwork))).Methods(http.MethodPut)

+ 2 - 2
controllers/node.go

@@ -28,9 +28,9 @@ func nodeHandlers(r *mux.Router) {
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", Authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods(http.MethodPost)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", Authorize(false, true, "user", checkFreeTierLimits(limitChoiceEgress, http.HandlerFunc(createEgressGateway)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", Authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createIngressGateway)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)

+ 16 - 4
controllers/server.go

@@ -24,12 +24,16 @@ func serverHandlers(r *mux.Router) {
 	r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/usage", Authorize(true, false, "user", http.HandlerFunc(getUsage))).Methods(http.MethodGet)
 }
+
+// TODO move to EE package? there is a function and a type there for that already
 func getUsage(w http.ResponseWriter, r *http.Request) {
 	type usage struct {
-		Hosts    int `json:"hosts"`
-		Clients  int `json:"clients"`
-		Networks int `json:"networks"`
-		Users    int `json:"users"`
+		Hosts     int `json:"hosts"`
+		Clients   int `json:"clients"`
+		Networks  int `json:"networks"`
+		Users     int `json:"users"`
+		Ingresses int `json:"ingresses"`
+		Egresses  int `json:"egresses"`
 	}
 	var serverUsage usage
 	hosts, err := logic.GetAllHosts()
@@ -48,6 +52,14 @@ func getUsage(w http.ResponseWriter, r *http.Request) {
 	if err == nil {
 		serverUsage.Networks = len(networks)
 	}
+	ingresses, err := logic.GetAllIngresses()
+	if err == nil {
+		serverUsage.Ingresses = len(ingresses)
+	}
+	egresses, err := logic.GetAllEgresses()
+	if err == nil {
+		serverUsage.Egresses = len(egresses)
+	}
 	w.Header().Set("Content-Type", "application/json")
 	json.NewEncoder(w).Encode(models.SuccessResponse{
 		Code:     http.StatusOK,

+ 1 - 1
controllers/user.go

@@ -30,7 +30,7 @@ func userHandlers(r *mux.Router) {
 	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(updateUser)))).Methods(http.MethodPut)
 	r.HandleFunc("/api/users/networks/{username}", logic.SecurityCheck(true, http.HandlerFunc(updateUserNetworks))).Methods(http.MethodPut)
 	r.HandleFunc("/api/users/{username}/adm", logic.SecurityCheck(true, http.HandlerFunc(updateUserAdm))).Methods(http.MethodPut)
-	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, checkFreeTierLimits(users_l, http.HandlerFunc(createUser)))).Methods(http.MethodPost)
+	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceUsers, http.HandlerFunc(createUser)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(deleteUser))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet)
 	r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).Methods(http.MethodGet)

+ 1 - 1
ee/license.go

@@ -81,7 +81,7 @@ func ValidateLicense() (err error) {
 
 	licenseSecret := LicenseSecret{
 		AssociatedID: netmakerTenantID,
-		Limits:       getCurrentServerLimit(),
+		Usage:        getCurrentServerUsage(),
 	}
 
 	secretData, err := json.Marshal(&licenseSecret)

+ 26 - 20
ee/types.go

@@ -24,15 +24,17 @@ var errValidation = fmt.Errorf(license_validation_err_msg)
 
 // LicenseKey - the license key struct representation with associated data
 type LicenseKey struct {
-	LicenseValue  string `json:"license_value"` // actual (public) key and the unique value for the key
-	Expiration    int64  `json:"expiration"`
-	LimitServers  int    `json:"limit_servers"`
-	LimitUsers    int    `json:"limit_users"`
-	LimitHosts    int    `json:"limit_hosts"`
-	LimitNetworks int    `json:"limit_networks"`
-	LimitClients  int    `json:"limit_clients"`
-	Metadata      string `json:"metadata"`
-	IsActive      bool   `json:"is_active"` // yes if active
+	LicenseValue   string `json:"license_value"` // actual (public) key and the unique value for the key
+	Expiration     int64  `json:"expiration"`
+	UsageServers   int    `json:"usage_servers"`
+	UsageUsers     int    `json:"usage_users"`
+	UsageClients   int    `json:"usage_clients"`
+	UsageHosts     int    `json:"usage_hosts"`
+	UsageNetworks  int    `json:"usage_networks"`
+	UsageIngresses int    `json:"usage_ingresses"`
+	UsageEgresses  int    `json:"usage_egresses"`
+	Metadata       string `json:"metadata"`
+	IsActive       bool   `json:"is_active"` // yes if active
 }
 
 // ValidatedLicense - the validated license struct
@@ -43,26 +45,30 @@ type ValidatedLicense struct {
 
 // LicenseSecret - the encrypted struct for sending user-id
 type LicenseSecret struct {
-	AssociatedID string        `json:"associated_id" binding:"required"` // UUID for user foreign key to User table
-	Limits       LicenseLimits `json:"limits" binding:"required"`
+	AssociatedID string `json:"associated_id" binding:"required"` // UUID for user foreign key to User table
+	Usage        Usage  `json:"usage" binding:"required"`
 }
 
-// LicenseLimits - struct license limits
-type LicenseLimits struct {
-	Servers  int `json:"servers"`
-	Users    int `json:"users"`
-	Hosts    int `json:"hosts"`
-	Clients  int `json:"clients"`
-	Networks int `json:"networks"`
+// Usage - struct for license usage
+type Usage struct {
+	Servers   int `json:"servers"`
+	Users     int `json:"users"`
+	Hosts     int `json:"hosts"`
+	Clients   int `json:"clients"`
+	Networks  int `json:"networks"`
+	Ingresses int `json:"ingresses"`
+	Egresses  int `json:"egresses"`
 }
 
-// LicenseLimits.SetDefaults - sets the default values for limits
-func (l *LicenseLimits) SetDefaults() {
+// Usage.SetDefaults - sets the default values for usage
+func (l *Usage) SetDefaults() {
 	l.Clients = 0
 	l.Servers = 1
 	l.Hosts = 0
 	l.Users = 1
 	l.Networks = 0
+	l.Ingresses = 0
+	l.Egresses = 0
 }
 
 // ValidateLicenseRequest - used for request to validate license endpoint

+ 14 - 5
ee/util.go

@@ -30,14 +30,15 @@ func base64decode(input string) []byte {
 
 	return bytes
 }
-func getCurrentServerLimit() (limits LicenseLimits) {
+
+func getCurrentServerUsage() (limits Usage) {
 	limits.SetDefaults()
-	hosts, err := logic.GetAllHosts()
-	if err == nil {
+	hosts, hErr := logic.GetAllHosts()
+	if hErr == nil {
 		limits.Hosts = len(hosts)
 	}
-	clients, err := logic.GetAllExtClients()
-	if err == nil {
+	clients, cErr := logic.GetAllExtClients()
+	if cErr == nil {
 		limits.Clients = len(clients)
 	}
 	users, err := logic.GetUsers()
@@ -48,5 +49,13 @@ func getCurrentServerLimit() (limits LicenseLimits) {
 	if err == nil {
 		limits.Networks = len(networks)
 	}
+	ingresses, err := logic.GetAllIngresses()
+	if err == nil {
+		limits.Ingresses = len(ingresses)
+	}
+	egresses, err := logic.GetAllEgresses()
+	if err == nil {
+		limits.Egresses = len(egresses)
+	}
 	return
 }

+ 36 - 12
logic/gateway.go

@@ -11,6 +11,36 @@ import (
 	"github.com/gravitl/netmaker/servercfg"
 )
 
+// GetAllIngresses - gets all the hosts that are ingresses
+func GetAllIngresses() ([]models.Node, error) {
+	nodes, err := GetAllNodes()
+	if err != nil {
+		return nil, err
+	}
+	ingresses := make([]models.Node, 0)
+	for _, node := range nodes {
+		if node.IsIngressGateway {
+			ingresses = append(ingresses, node)
+		}
+	}
+	return ingresses, nil
+}
+
+// GetAllEgresses - gets all the hosts that are egresses
+func GetAllEgresses() ([]models.Node, error) {
+	nodes, err := GetAllNodes()
+	if err != nil {
+		return nil, err
+	}
+	egresses := make([]models.Node, 0)
+	for _, node := range nodes {
+		if node.IsEgressGateway {
+			egresses = append(egresses, node)
+		}
+	}
+	return egresses, nil
+}
+
 // CreateEgressGateway - creates an egress gateway
 func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, error) {
 	node, err := GetNodeByID(gateway.NodeID)
@@ -28,10 +58,13 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 		return models.Node{}, errors.New("firewall is not supported for egress gateways")
 	}
 	for i := len(gateway.Ranges) - 1; i >= 0; i-- {
+		// check if internet gateway IPv4
+		if gateway.Ranges[i] == "0.0.0.0/0" && FreeTier {
+			return models.Node{}, fmt.Errorf("currently IPv4 internet gateways are not supported on the free tier: %s", gateway.Ranges[i])
+		}
+		// check if internet gateway IPv6
 		if gateway.Ranges[i] == "::/0" {
-			logger.Log(0, "currently IPv6 internet gateways are not supported", gateway.Ranges[i])
-			gateway.Ranges = append(gateway.Ranges[:i], gateway.Ranges[i+1:]...)
-			continue
+			return models.Node{}, fmt.Errorf("currently IPv6 internet gateways are not supported: %s", gateway.Ranges[i])
 		}
 		normalized, err := NormalizeCIDR(gateway.Ranges[i])
 		if err != nil {
@@ -150,15 +183,6 @@ func DeleteIngressGateway(nodeid string) (models.Node, bool, []models.ExtClient,
 	node.IsIngressGateway = false
 	node.IngressGatewayRange = ""
 	node.Failover = false
-
-	//logger.Log(3, "deleting ingress gateway firewall in use is '", host.FirewallInUse, "' and isEgressGateway is", node.IsEgressGateway)
-	if node.EgressGatewayRequest.NodeID != "" {
-		_, err := CreateEgressGateway(node.EgressGatewayRequest)
-		if err != nil {
-			logger.Log(0, fmt.Sprintf("failed to create egress gateway on node [%s] on network [%s]: %v",
-				node.EgressGatewayRequest.NodeID, node.EgressGatewayRequest.NetID, err))
-		}
-	}
 	err = UpsertNode(&node)
 	if err != nil {
 		return models.Node{}, wasFailover, removedClients, err

+ 7 - 7
logic/hosts.go

@@ -158,14 +158,14 @@ func GetHost(hostid string) (*models.Host, error) {
 
 // CreateHost - creates a host if not exist
 func CreateHost(h *models.Host) error {
-	hosts, err := GetAllHosts()
-	if err != nil && !database.IsEmptyRecord(err) {
-		return err
+	hosts, hErr := GetAllHosts()
+	clients, cErr := GetAllExtClients()
+	if (hErr != nil && !database.IsEmptyRecord(hErr)) ||
+		(cErr != nil && !database.IsEmptyRecord(cErr)) ||
+		len(hosts)+len(clients) >= MachinesLimit {
+		return errors.New("free tier limits exceeded on machines")
 	}
-	if len(hosts) >= Hosts_Limit {
-		return errors.New("free tier limits exceeded on hosts")
-	}
-	_, err = GetHost(h.ID.String())
+	_, err := GetHost(h.ID.String())
 	if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
 		return ErrHostExists
 	}

+ 19 - 16
logic/serverconf.go

@@ -2,22 +2,23 @@ package logic
 
 import (
 	"encoding/json"
-
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/servercfg"
 )
 
 var (
-	// Networks_Limit - dummy var for community
-	Networks_Limit = 1000000000
-	// Users_Limit - dummy var for community
-	Users_Limit = 1000000000
-	// Clients_Limit - dummy var for community
-	Clients_Limit = 1000000000
-	// Hosts_Limit - dummy var for community
-	Hosts_Limit = 1000000000
-	// Free_Tier - specifies if free tier
-	Free_Tier = false
+	// NetworksLimit - dummy var for community
+	NetworksLimit = 1000000000
+	// UsersLimit - dummy var for community
+	UsersLimit = 1000000000
+	// MachinesLimit - dummy var for community
+	MachinesLimit = 1000000000
+	// IngressesLimit - dummy var for community
+	IngressesLimit = 1000000000
+	// EgressesLimit - dummy var for community
+	EgressesLimit = 1000000000
+	// FreeTier - specifies if free tier
+	FreeTier = false
 )
 
 type serverData struct {
@@ -87,10 +88,12 @@ func StoreJWTSecret(privateKey string) error {
 	return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME)
 }
 
+// SetFreeTierLimits - sets limits for free tier
 func SetFreeTierLimits() {
-	Free_Tier = true
-	Users_Limit = servercfg.GetUserLimit()
-	Clients_Limit = servercfg.GetClientLimit()
-	Networks_Limit = servercfg.GetNetworkLimit()
-	Hosts_Limit = servercfg.GetHostLimit()
+	FreeTier = true
+	UsersLimit = servercfg.GetUserLimit()
+	NetworksLimit = servercfg.GetNetworkLimit()
+	MachinesLimit = servercfg.GetMachinesLimit()
+	IngressesLimit = servercfg.GetIngressLimit()
+	EgressesLimit = servercfg.GetEgressLimit()
 }

+ 18 - 16
servercfg/serverconf.go

@@ -753,26 +753,28 @@ func GetNetworkLimit() int {
 	return networkslimit
 }
 
-// GetClientLimit - fetches free tier limits on ext. clients
-func GetClientLimit() int {
-	var clientsLimit int
-	if os.Getenv("CLIENTS_LIMIT") != "" {
-		clientsLimit, _ = strconv.Atoi(os.Getenv("CLIENTS_LIMIT"))
-	} else {
-		clientsLimit = config.Config.Server.ClientsLimit
+// GetMachinesLimit - fetches free tier limits on machines (clients + hosts)
+func GetMachinesLimit() int {
+	if l, err := strconv.Atoi(os.Getenv("MACHINES_LIMIT")); err == nil {
+		return l
 	}
-	return clientsLimit
+	return config.Config.Server.MachinesLimit
 }
 
-// GetHostLimit - fetches free tier limits on hosts
-func GetHostLimit() int {
-	var hostsLimit int
-	if os.Getenv("HOSTS_LIMIT") != "" {
-		hostsLimit, _ = strconv.Atoi(os.Getenv("HOSTS_LIMIT"))
-	} else {
-		hostsLimit = config.Config.Server.HostsLimit
+// GetIngressLimit - fetches free tier limits on ingresses
+func GetIngressLimit() int {
+	if l, err := strconv.Atoi(os.Getenv("INGRESSES_LIMIT")); err == nil {
+		return l
+	}
+	return config.Config.Server.IngressesLimit
+}
+
+// GetEgressLimit - fetches free tier limits on egresses
+func GetEgressLimit() int {
+	if l, err := strconv.Atoi(os.Getenv("EGRESSES_LIMIT")); err == nil {
+		return l
 	}
-	return hostsLimit
+	return config.Config.Server.EgressesLimit
 }
 
 // DeployedByOperator - returns true if the instance is deployed by netmaker operator