Merge pull request #662 from openziti/frontends_resource_count

Add 'frontends' to Resource Count Classes (#650)
This commit is contained in:
Michael Quigley 2024-06-18 10:57:44 -04:00 committed by GitHub
commit ccc969523e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 65 additions and 23 deletions

View File

@ -2,6 +2,8 @@
## v0.4.32
FEATURE: Resource count limits now include `share_frontends` to limit the number of frontends that are allowed to make connections to a share (https://github.com/openziti/zrok/issues/650)
FIX: use controller config spec v4 in the Docker instance
## v0.4.31

View File

@ -163,30 +163,47 @@ func (a *Agent) CanAccessShare(shrId int, trx *sqlx.Tx) (bool, error) {
return false, err
}
if env.AccountId != nil {
if err := a.str.LimitCheckLock(*env.AccountId, trx); err != nil {
return false, err
}
ul, err := a.getUserLimits(*env.AccountId, trx)
if err != nil {
return false, err
}
if ul.resource.IsGlobal() {
if empty, err := a.str.IsBandwidthLimitJournalEmptyForGlobal(*env.AccountId, trx); err == nil && !empty {
lj, err := a.str.FindLatestBandwidthLimitJournalForGlobal(*env.AccountId, trx)
if scopedBwc, found := ul.scopes[sdk.BackendMode(shr.BackendMode)]; found {
latestScopedJe, err := a.isBandwidthClassLimitedForAccount(*env.AccountId, scopedBwc, trx)
if err != nil {
return false, err
}
if latestScopedJe != nil {
return false, nil
}
} else {
for _, bwc := range ul.bandwidth {
latestJe, err := a.isBandwidthClassLimitedForAccount(*env.AccountId, bwc, trx)
if err != nil {
return false, err
}
if lj.Action == store.LimitLimitAction {
if latestJe != nil {
return false, nil
}
}
} else {
if empty, err := a.str.IsBandwidthLimitJournalEmptyForLimitClass(*env.AccountId, ul.resource.GetLimitClassId(), trx); err == nil && !empty {
lj, err := a.str.FindLatestBandwidthLimitJournalForLimitClass(*env.AccountId, ul.resource.GetLimitClassId(), trx)
if err != nil {
return false, err
}
if lj.Action == store.LimitLimitAction {
return false, nil
}
}
rc := ul.resource
if scopeRc, found := ul.scopes[sdk.BackendMode(shr.BackendMode)]; found {
rc = scopeRc
}
if rc.GetShareFrontends() > store.Unlimited {
fes, err := a.str.FindFrontendsForPrivateShare(shr.Id, trx)
if err != nil {
return false, err
}
if len(fes)+1 > rc.GetShareFrontends() {
logrus.Infof("account '#%d' over frontends per share limit '%d'", *env.AccountId, rc.GetReservedShares())
return false, nil
}
}
} else {

View File

@ -10,6 +10,7 @@ type Config struct {
Shares int
ReservedShares int
UniqueNames int
ShareFrontends int
Bandwidth *BandwidthPerPeriod
Cycle time.Duration
Enforcing bool
@ -49,6 +50,7 @@ func DefaultConfig() *Config {
Shares: store.Unlimited,
ReservedShares: store.Unlimited,
UniqueNames: store.Unlimited,
ShareFrontends: store.Unlimited,
Bandwidth: DefaultBandwidthPerPeriod(),
Enforcing: false,
Cycle: 15 * time.Minute,

View File

@ -37,6 +37,10 @@ func (rcc *configResourceCountClass) GetUniqueNames() int {
return rcc.cfg.UniqueNames
}
func (rcc *configResourceCountClass) String() string {
return fmt.Sprintf("Config<environments: %d, shares: %d, reservedShares: %d, uniqueNames: %d>", rcc.cfg.Environments, rcc.cfg.Shares, rcc.cfg.ReservedShares, rcc.cfg.UniqueNames)
func (rcc *configResourceCountClass) GetShareFrontends() int {
return rcc.cfg.ShareFrontends
}
func (rcc *configResourceCountClass) String() string {
return fmt.Sprintf("Config<environments: %d, shares: %d, reservedShares: %d, uniqueNames: %d, share_frontends: %d>", rcc.cfg.Environments, rcc.cfg.Shares, rcc.cfg.ReservedShares, rcc.cfg.UniqueNames, rcc.cfg.ShareFrontends)
}

View File

@ -42,7 +42,6 @@ func (ul *userLimits) ignoreBackends(bwc store.BandwidthClass) map[sdk.BackendMo
}
return ignoreBackends
}
return nil
}
func (a *Agent) getUserLimits(acctId int, trx *sqlx.Tx) (*userLimits, error) {
@ -85,7 +84,7 @@ func (a *Agent) isResourceCountClass(alc *store.LimitClass) bool {
if alc.BackendMode != nil {
return false
}
if alc.Environments == store.Unlimited && alc.Shares == store.Unlimited && alc.ReservedShares == store.Unlimited && alc.UniqueNames == store.Unlimited {
if alc.Environments == store.Unlimited && alc.Shares == store.Unlimited && alc.ReservedShares == store.Unlimited && alc.UniqueNames == store.Unlimited && alc.ShareFrontends == store.Unlimited {
return false
}
return true
@ -95,7 +94,7 @@ func (a *Agent) isUnscopedBandwidthClass(alc *store.LimitClass) bool {
if alc.BackendMode != nil {
return false
}
if alc.Environments > store.Unlimited || alc.Shares > store.Unlimited || alc.ReservedShares > store.Unlimited || alc.UniqueNames > store.Unlimited {
if alc.Environments > store.Unlimited || alc.Shares > store.Unlimited || alc.ReservedShares > store.Unlimited || alc.UniqueNames > store.Unlimited || alc.ShareFrontends > store.Unlimited {
return false
}
if alc.PeriodMinutes < 1 {

View File

@ -22,6 +22,7 @@ type ResourceCountClass interface {
GetShares() int
GetReservedShares() int
GetUniqueNames() int
GetShareFrontends() int
}
type BandwidthClass interface {
@ -42,6 +43,7 @@ type LimitClass struct {
Shares int
ReservedShares int
UniqueNames int
ShareFrontends int
PeriodMinutes int
RxBytes int64
TxBytes int64
@ -77,6 +79,10 @@ func (lc LimitClass) GetUniqueNames() int {
return lc.UniqueNames
}
func (lc LimitClass) GetShareFrontends() int {
return lc.ShareFrontends
}
func (lc LimitClass) GetBackendMode() sdk.BackendMode {
if lc.BackendMode == nil {
return ""
@ -121,6 +127,9 @@ func (lc LimitClass) String() string {
if lc.UniqueNames > Unlimited {
out += fmt.Sprintf(", uniqueNames: %d", lc.UniqueNames)
}
if lc.ShareFrontends > Unlimited {
out += fmt.Sprintf(", shareFrontends: %d", lc.ShareFrontends)
}
if lc.RxBytes > Unlimited || lc.TxBytes > Unlimited || lc.TotalBytes > Unlimited {
out += fmt.Sprintf(", periodMinutes: %d", lc.PeriodMinutes)
}
@ -140,12 +149,12 @@ func (lc LimitClass) String() string {
var _ BandwidthClass = (*LimitClass)(nil)
func (str *Store) CreateLimitClass(lc *LimitClass, trx *sqlx.Tx) (int, error) {
stmt, err := trx.Prepare("insert into limit_classes (backend_mode, environments, shares, reserved_shares, unique_names, period_minutes, rx_bytes, tx_bytes, total_bytes, limit_action) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) returning id")
stmt, err := trx.Prepare("insert into limit_classes (backend_mode, environments, shares, reserved_shares, unique_names, share_frontends, period_minutes, rx_bytes, tx_bytes, total_bytes, limit_action) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) returning id")
if err != nil {
return 0, errors.Wrap(err, "error preparing limit_classes insert statement")
}
var id int
if err := stmt.QueryRow(lc.BackendMode, lc.Environments, lc.Shares, lc.ReservedShares, lc.UniqueNames, lc.PeriodMinutes, lc.RxBytes, lc.TxBytes, lc.TotalBytes, lc.LimitAction).Scan(&id); err != nil {
if err := stmt.QueryRow(lc.BackendMode, lc.Environments, lc.Shares, lc.ReservedShares, lc.UniqueNames, lc.ShareFrontends, lc.PeriodMinutes, lc.RxBytes, lc.TxBytes, lc.TotalBytes, lc.LimitAction).Scan(&id); err != nil {
return 0, errors.Wrap(err, "error executing limit_classes insert statement")
}
return id, nil

View File

@ -0,0 +1,3 @@
-- +migrate Up
alter table limit_classes add column share_frontends int not null default (-1);

View File

@ -0,0 +1,3 @@
-- +migrate Up
alter table limit_classes add column share_frontends int not null default (-1);

View File

@ -20,7 +20,7 @@ The limits agent is responsible for controlling the number of resources in use (
### Types of Limits
Limits can be specified that control the number of environments, shares, reserved shares, and unique names that can be created by an account. Limits that control the allowed number of resources are called _resource count limits_.
Limits can be specified that control the number of environments, shares, reserved shares, unique names, and frontends per-share that can be created by an account. Limits that control the allowed number of resources are called _resource count limits_.
Limits can be specified to control the amount of data that can be transferred within a time period. Limits that control the amount of data that can be transferred are called _bandwidth limits_.
@ -40,6 +40,7 @@ limits:
shares: -1
reserved_shares: -1
unique_names: -1
share_frontends: -1
bandwidth:
period: 5m
warning:
@ -64,7 +65,7 @@ The `cycle` value controls how frequently the limits agent will evaluate enforce
### Global Resouce Count Limits
The `environments`, `shares`, `reserved_shares`, and `unique_names` specify the resource count limits, globally for the service instance.
The `environments`, `shares`, `reserved_shares`, `unique_names`, and `share_frontends` specify the resource count limits, globally for the service instance.
These resource counts will be applied to all users in the service instance by default.
@ -96,6 +97,7 @@ CREATE TABLE public.limit_classes (
shares integer DEFAULT '-1'::integer NOT NULL,
reserved_shares integer DEFAULT '-1'::integer NOT NULL,
unique_names integer DEFAULT '-1'::integer NOT NULL,
share_frontends integer DEFAULT '-1'::integer NOT NULL,
period_minutes integer DEFAULT 1440 NOT NULL,
rx_bytes bigint DEFAULT '-1'::integer NOT NULL,
tx_bytes bigint DEFAULT '-1'::integer NOT NULL,
@ -130,7 +132,7 @@ Create a row in this table linking the `account_id` to the `limit_class_id` to a
To support overriding the resource count limits defined in the global limits configuration, a site administrator can create a limit class by inserting a row into the `limit_classes` table structured like this:
```sql
insert into limit_classes (environments, shares, reserved_shares, unique_names) values (1, 1, 1, 1);
insert into limit_classes (environments, shares, reserved_shares, unique_names, share_frontends) values (1, 1, 1, 1, 1);
```
This creates a limit class that sets the `environments`, `shares`, `reserved_shares`, and `unique_names` all to `1`.

View File

@ -83,6 +83,7 @@ limits:
shares: -1
reserved_shares: -1
unique_names: -1
share_frontends: -1
bandwidth:
period: 5m
warning: