Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PER_USER leasing strategy to policies #838

Merged
merged 1 commit into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,18 @@ To run Cucumber features, run:

```bash
bundle exec rake test:cucumber
bundle exec rake test:cucumber[features/api/v1/licenses/create.feature]
bundle exec rake test:cucumber[features/api/v1/licenses/actions/validations.feature:369]
bundle exec rake test:cucumber[features/api/v1/licenses/actions/validations.feature]
bundle exec rake test:cucumber[features/api/v1/licenses]
```

To run Rspec specs, run:

```bash
bundle exec rake test:rspec
bundle exec rake test:rspec[spec/models/license_spec.rb:199]
bundle exec rake test:rspec[spec/models/license_spec.rb]
bundle exec rake test:rspec[spec/models]
```

## License
Expand Down
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ def render_unprocessable_resource(resource)
in source: { pointer: %r{/attributes/([^/]+)} }
s << '-attrs-' << $1
else
s
end
}

Expand Down
41 changes: 11 additions & 30 deletions app/models/license.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class License < ApplicationRecord
next if license.uses.nil? || license.max_uses.nil?
next if license.uses <= license.max_uses

license.errors.add :uses, :limit_exceeded, message: "usage exceeds maximum allowed by current policy (#{license.max_uses})"
license.errors.add :uses, :limit_exceeded, message: "usage exceeds maximum allowed for license (#{license.max_uses})"
end

validate on: :update do |license|
Expand Down Expand Up @@ -607,7 +607,7 @@ class License < ApplicationRecord
:strict?, :pool?, :node_locked?, :floating?,
:always_allow_overage?, :allow_overage?, :allow_1_25x_overage?, :allow_1_5x_overage?, :allow_2x_overage?, :no_overage?,
:revoke_access?, :restrict_access?, :maintain_access?, :allow_access?,
:lease_per_machine?, :lease_per_license?,
:lease_per_machine?, :lease_per_license?, :lease_per_user?,
:expire_from_creation?,
:expire_from_first_validation?,
:expire_from_first_activation?,
Expand Down Expand Up @@ -722,50 +722,31 @@ def status
end
end


def max_machines = max_machines_override? ? max_machines_override : policy&.max_machines
def max_machines? = max_machines.present?
def max_machines=(value)
self.max_machines_override = value
end

def max_machines
return max_machines_override if
max_machines_override?

policy&.max_machines
end

def max_cores = max_cores_override? ? max_cores_override : policy&.max_cores
def max_cores? = max_cores.present?
def max_cores=(value)
self.max_cores_override = value
end

def max_cores
return max_cores_override if
max_cores_override?

policy&.max_cores
end

def max_uses = max_uses_override? ? max_uses_override : policy&.max_uses
def max_uses? = max_uses.present?
def max_uses=(value)
self.max_uses_override = value
end

def max_uses
return max_uses_override if
max_uses_override?

policy&.max_uses
end

def max_processes = max_processes_override? ? max_processes_override : policy&.max_processes
def max_processes? = max_processes.present?
def max_processes=(value)
self.max_processes_override = value
end

def max_processes
return max_processes_override if
max_processes_override?

policy&.max_processes
end

def max_users = max_users_override? ? max_users_override : policy&.max_users
def max_users? = max_users.present?
def max_users=(value)
Expand Down
114 changes: 82 additions & 32 deletions app/models/machine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,52 +122,102 @@ class ResurrectionExpiredError < StandardError; end
validate on: :create do |machine|
next if machine.license.nil?
next if
machine.license.max_machines.nil? || machine.license.always_allow_overage?
machine.license.always_allow_overage?

prev_machines_count = machine.license.machines_count || 0
next if
prev_machines_count == 0

next_machine_count = prev_machines_count + 1
next unless
next_machine_count > machine.license.max_machines
machine.license.max_machines?

next if
machine.license.allow_1_25x_overage? && next_machine_count <= machine.license.max_machines * 1.25
case
when license.lease_per_machine?,
license.lease_per_license?
prev_machines_count = machine.license.machines_count || 0
next if
prev_machines_count == 0

next if
machine.license.allow_1_5x_overage? && next_machine_count <= machine.license.max_machines * 1.5
next_machine_count = prev_machines_count + 1
next unless
next_machine_count > machine.license.max_machines

next if
machine.license.allow_2x_overage? && next_machine_count <= machine.license.max_machines * 2
next if
machine.license.allow_1_25x_overage? && next_machine_count <= machine.license.max_machines * 1.25

machine.errors.add :base, :limit_exceeded, message: "machine count has exceeded maximum allowed by current policy (#{machine.license.max_machines})"
end
next if
machine.license.allow_1_5x_overage? && next_machine_count <= machine.license.max_machines * 1.5

# Disallow machine core overages according to policy overage strategy
validate on: [:create, :update] do |machine|
next if machine.license.nil?
next if
machine.license.max_cores.nil? || machine.license.always_allow_overage?
next if
machine.license.allow_2x_overage? && next_machine_count <= machine.license.max_machines * 2

prev_core_count = machine.license.machines.where.not(id: machine.id).sum(:cores) || 0
next_core_count = prev_core_count + machine.cores.to_i
next if
next_core_count == 0
machine.errors.add :base, :limit_exceeded, message: "machine count has exceeded maximum allowed for license (#{machine.license.max_machines})"
when license.lease_per_user?
prev_machines_count = license.machines.where(owner:) # nil owner is significant
.count
next if
prev_machines_count == 0

next unless
next_core_count > machine.license.max_cores
next_machine_count = prev_machines_count + 1
next unless
next_machine_count > machine.license.max_machines

next if
machine.license.allow_1_25x_overage? && next_core_count <= machine.license.max_cores * 1.25
next if
machine.license.allow_1_25x_overage? && next_machine_count <= machine.license.max_machines * 1.25

next if
machine.license.allow_1_5x_overage? && next_core_count <= machine.license.max_cores * 1.5
next if
machine.license.allow_1_5x_overage? && next_machine_count <= machine.license.max_machines * 1.5

next if
machine.license.allow_2x_overage? && next_machine_count <= machine.license.max_machines * 2

machine.errors.add :base, :limit_exceeded, message: "machine count has exceeded maximum allowed for user (#{machine.license.max_machines})"
end
end

# Disallow machine core overages according to policy overage strategy
validate on: %i[create update] do |machine|
next if machine.license.nil?
next if
machine.license.allow_2x_overage? && next_core_count <= machine.license.max_cores * 2
machine.license.always_allow_overage?

next unless
machine.license.max_cores?

machine.errors.add :base, :core_limit_exceeded, message: "machine core count has exceeded maximum allowed by current policy (#{machine.license.max_cores})"
case
when license.lease_per_machine?,
license.lease_per_license?
prev_core_count = machine.license.machines.where.not(id: machine.id)
.sum(:cores)
next_core_count = prev_core_count + machine.cores.to_i
next unless
next_core_count > machine.license.max_cores

next if
machine.license.allow_1_25x_overage? && next_core_count <= machine.license.max_cores * 1.25

next if
machine.license.allow_1_5x_overage? && next_core_count <= machine.license.max_cores * 1.5

next if
machine.license.allow_2x_overage? && next_core_count <= machine.license.max_cores * 2

machine.errors.add :base, :core_limit_exceeded, message: "machine core count has exceeded maximum allowed for license (#{machine.license.max_cores})"
when license.lease_per_user?
prev_core_count = machine.license.machines.where.not(id: machine.id)
.where(owner:) # nil owner is significant
.sum(:cores)
next_core_count = prev_core_count + machine.cores.to_i
next unless
next_core_count > machine.license.max_cores

next if
machine.license.allow_1_25x_overage? && next_core_count <= machine.license.max_cores * 1.25

next if
machine.license.allow_1_5x_overage? && next_core_count <= machine.license.max_cores * 1.5

next if
machine.license.allow_2x_overage? && next_core_count <= machine.license.max_cores * 2

machine.errors.add :base, :core_limit_exceeded, message: "machine core count has exceeded maximum allowed for user (#{machine.license.max_cores})"
end
end

# Fingerprint uniqueness on create
Expand Down
19 changes: 18 additions & 1 deletion app/models/machine_process.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ResurrectionExpiredError < StandardError; end
before_validation -> { self.last_heartbeat_at ||= Time.current },
on: :create

delegate :leasing_strategy, :lease_per_license?, :lease_per_machine?,
delegate :leasing_strategy, :lease_per_license?, :lease_per_machine?, :lease_per_user?,
:resurrect_dead?, :always_resurrect_dead?, :lazarus_ttl,
allow_nil: true,
to: :policy
Expand Down Expand Up @@ -89,6 +89,23 @@ class ResurrectionExpiredError < StandardError; end
license.allow_2x_overage? && next_process_count <= license.max_processes * 2

errors.add :base, :limit_exceeded, message: "process count has exceeded maximum allowed for license (#{license.max_processes})"
when lease_per_user?
next_process_count = license.processes.left_outer_joins(:owner)
.where(owner: { id: owner }) # nil owner is significant
.count + 1
next unless
next_process_count > license.max_processes

next if
license.allow_1_25x_overage? && next_process_count <= license.max_processes * 1.25

next if
license.allow_1_5x_overage? && next_process_count <= license.max_processes * 1.5

next if
license.allow_2x_overage? && next_process_count <= license.max_processes * 2

errors.add :base, :limit_exceeded, message: "process count has exceeded maximum allowed for user (#{license.max_processes})"
end
end

Expand Down
5 changes: 5 additions & 0 deletions app/models/policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class EmptyPoolError < StandardError; end
LEASING_STRATEGIES = %w[
PER_LICENSE
PER_MACHINE
PER_USER
].freeze

OVERAGE_STATEGIES = %w[
Expand Down Expand Up @@ -603,6 +604,10 @@ def lease_per_machine?
leasing_strategy == 'PER_MACHINE'
end

def lease_per_user?
leasing_strategy == 'PER_USER'
end

def always_allow_overage?
overage_strategy == 'ALWAYS_ALLOW_OVERAGE'
end
Expand Down
Loading