foreman_resource_quota 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/app/controllers/foreman_resource_quota/api/v2/resource_quotas_controller.rb +1 -1
  4. data/app/controllers/foreman_resource_quota/concerns/api/v2/hosts_controller_extensions.rb +21 -0
  5. data/app/helpers/foreman_resource_quota/hosts_helper.rb +18 -7
  6. data/app/lib/foreman_resource_quota/exceptions.rb +1 -0
  7. data/app/models/concerns/foreman_resource_quota/host_managed_extensions.rb +9 -10
  8. data/app/models/concerns/foreman_resource_quota/user_extensions.rb +27 -0
  9. data/app/models/concerns/foreman_resource_quota/usergroup_extensions.rb +18 -0
  10. data/app/models/foreman_resource_quota/resource_quota.rb +23 -0
  11. data/app/views/foreman_resource_quota/resource_quotas/index.html.erb +9 -1
  12. data/app/views/hosts/_form_quota_fields.html.erb +14 -2
  13. data/app/views/users/_form_quota_tab.html.erb +2 -2
  14. data/db/migrate/20240611141939_drop_missing_hosts.rb +9 -2
  15. data/db/migrate/20250410082728_add_unassigned_flag_to_resource_quota.rb +7 -0
  16. data/db/seeds.d/030-unassigned_quota.rb +36 -0
  17. data/lib/foreman_resource_quota/register.rb +8 -0
  18. data/lib/foreman_resource_quota/version.rb +1 -1
  19. data/lib/tasks/foreman_resource_quota_tasks.rake +4 -5
  20. data/package.json +9 -10
  21. data/webpack/components/CreateResourceQuotaModal.js +1 -0
  22. data/webpack/components/ResourceQuotaEmptyState/__test__/__snapshots__/ResourceQuotaEmptyState.test.js.snap +4 -0
  23. data/webpack/components/ResourceQuotaEmptyState/index.js +1 -0
  24. data/webpack/components/ResourceQuotaForm/ResourceQuotaForm.scss +1 -1
  25. data/webpack/components/ResourceQuotaForm/ResourceQuotaFormConstants.js +1 -0
  26. data/webpack/components/ResourceQuotaForm/components/Properties/Properties.scss +4 -3
  27. data/webpack/components/ResourceQuotaForm/components/Properties/StaticDetail.js +3 -2
  28. data/webpack/components/ResourceQuotaForm/components/Properties/TextInputField.js +4 -1
  29. data/webpack/components/ResourceQuotaForm/components/Properties/index.js +86 -45
  30. data/webpack/components/ResourceQuotaForm/components/Resource/Resource.scss +5 -5
  31. data/webpack/components/ResourceQuotaForm/components/Resource/UnitInputField.js +84 -37
  32. data/webpack/components/ResourceQuotaForm/components/Resource/UnitInputField.scss +7 -0
  33. data/webpack/components/ResourceQuotaForm/components/Resource/UtilizationProgress.js +14 -13
  34. data/webpack/components/ResourceQuotaForm/components/Resource/UtilizationProgress.scss +4 -4
  35. data/webpack/components/ResourceQuotaForm/components/Resource/__test__/__snapshots__/UnitInputField.test.js.snap +149 -140
  36. data/webpack/components/ResourceQuotaForm/components/Resource/index.js +28 -17
  37. data/webpack/components/ResourceQuotaForm/components/Submit.js +2 -1
  38. data/webpack/components/ResourceQuotaForm/index.js +33 -19
  39. data/webpack/components/UpdateResourceQuotaModal.js +7 -1
  40. data/webpack/lib/ActionableDetail.scss +1 -1
  41. data/webpack/lib/EditableSwitch.js +1 -1
  42. data/webpack/lib/EditableTextInput/EditableTextInput.js +81 -77
  43. data/webpack/lib/EditableTextInput/editableTextInput.scss +30 -28
  44. metadata +8 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46b1ed89e58274a5cfb37c6c32544151202c8b80e921560e5617cb132f5b7188
4
- data.tar.gz: b04271957baf9f86d79281f0b5834733349fe09163d3ccdd4136d19644c396e1
3
+ metadata.gz: e395e2dfdbaf6a690ee47ed3885daa6d1bf02d66fa9ab4ddc9616214913e1bbe
4
+ data.tar.gz: c6804f4c3ac2245a891f54ddbec65b5736983466c05e18fedc3d0b6ff926c932
5
5
  SHA512:
6
- metadata.gz: 96e7878190f41b66998e8c2f58412e5b4b5db2bd89271b4c42760c1d5894eff7d0dcf5890d9ff9f1231cb3e972e5cc4066b26cfd90899652122a854ebcfe2d74
7
- data.tar.gz: b14c3f7584293897ad9d558ff27ed89f9f1fc24b41c3820e6aca8e961ad02016d23fce9aabd8562b9ebbdf5bb59fd17f3a466ec059370fefa189c5abbae3092c
6
+ metadata.gz: 54de1fc3feda746af897c9161a3079f12e6e0be2ad9ef372ab39a9987cb86866fc7216bd5df13bcc477beb8ec8587be81bd95f858e65e05f034a22d78e88fa93
7
+ data.tar.gz: '09bf9301c5b261f83e5ca90d78ae0d38b1eff943c0c680d5c8fbd6eeca080f18c8e744f29a58f753b42f35c5c1bd52f04f0a1a562384ebdfa07dd96796cc31f0'
data/README.md CHANGED
@@ -14,20 +14,20 @@ For more information, see [Limiting host resources](https://docs.theforeman.org/
14
14
 
15
15
  | Foreman Version | Plugin Version |
16
16
  | --------------- | -------------- |
17
- | 3.11 | 0.3.1 |
17
+ | 3.15 | 0.5.0 |
18
+ | 3.14 | 0.4.0 |
19
+ | 3.13 | ~> 0.3.0 |
18
20
  | 3.5 | 0.0.1 |
19
21
 
20
22
  ## Usage
21
23
 
22
- _TODO_ Still under development: Official documentation will be added soon.
23
-
24
24
  When several users share a compute resource or infrastructure, there is a concern that some users could use more than its fair share of resources. Resource Quotas are a tool for administrators to address this concern. They limit access to the shared resource in order to guarantee a fair collaboration.
25
25
 
26
26
  In the context of Foreman, multiple users or groups usually share a fixed number of resources (limitation of compute resources like CPU cores, memory, and disk space). As of now, a user cannot be limited when allocating resources. They can create hosts with as many resources as they want. This could lead to over-usage or unequal balancing of resources under the users.
27
27
 
28
28
  This plugin introduces the configuration of Resource Quotas. A quota limits specific resources and can be applied to a user or a user group. If a user belongs to a user group, the group’s quota is automatically applied to the user as well. When deploying a new host, a user has to choose a Resource Quota that the host counts to.
29
29
 
30
- A user is hindered from deploying new hosts, if the new host would exceed the corresponding quota limits. In case, a user belongs to multiple user group with quota, the user can determine which quota new hosts belong to.
30
+ A user is hindered from deploying new hosts, if the new host would exceed the corresponding quota limits. In case, a user belongs to multiple user group with quota, the user can determine which quota new hosts belong to.
31
31
 
32
32
 
33
33
  ## Contributing
@@ -42,7 +42,7 @@ Fork and send a Pull Request. Thanks!
42
42
 
43
43
  ## Copyright
44
44
 
45
- Copyright (c) 2023 ATIX AG - https://atix.de
45
+ Copyright (c) 2025 ATIX AG - https://atix.de
46
46
 
47
47
  This program is free software: you can redistribute it and/or modify
48
48
  it under the terms of the GNU General Public License as published by
@@ -40,7 +40,7 @@ module ForemanResourceQuota
40
40
  end
41
41
 
42
42
  api :GET, '/resource_quotas/:id/missing_hosts',
43
- N_('Show resources could not be determined when calculating utilization')
43
+ N_("Show hosts' resources that could not be determined when calculating the quota utilization")
44
44
  param :id, :identifier, required: true
45
45
  def missing_hosts
46
46
  process_response @resource_quota
@@ -5,7 +5,14 @@ module ForemanResourceQuota
5
5
  module Api
6
6
  module V2
7
7
  module HostsControllerExtensions
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ before_action :check_if_quota_is_set, only: %i[create update]
12
+ end
13
+
8
14
  extend ::Apipie::DSL::Concern
15
+
9
16
  update_api(:create, :update) do
10
17
  param :host, Hash do
11
18
  param :resource_quota_id, :number, required: false,
@@ -13,6 +20,20 @@ module ForemanResourceQuota
13
20
  This field is required if the setting `resource_quota_optional_assignment` is set to false.')
14
21
  end
15
22
  end
23
+
24
+ private
25
+
26
+ def check_if_quota_is_set # rubocop:disable Metrics/AbcSize
27
+ return if User.current.quota_assignment_optional?
28
+ quota = if User.current&.admin?
29
+ ResourceQuota.where(id: params['host']['resource_quota_id']).first
30
+ else
31
+ User.current.resource_quotas.where(id: params['host']['resource_quota_id']).first
32
+ end
33
+ return unless quota.nil? || quota.unassigned?
34
+ render_error :custom_error, status: :unprocessable_entity,
35
+ locals: { message: 'No valid resource quota provided' }
36
+ end
16
37
  end
17
38
  end
18
39
  end
@@ -2,17 +2,28 @@
2
2
 
3
3
  module ForemanResourceQuota
4
4
  module HostsHelper
5
- def resource_quota_select(form, user_quotas)
6
- blank_opt = { include_blank: true }
7
- select_items = user_quotas.order(:name)
5
+ def resource_quota_select(form, user_quotas, selected, assignment_optional, host_quota)
6
+ select_opts = { include_blank: false,
7
+ selected: selected }
8
+ html_opts = { label: _('Resource Quota'),
9
+ required: !assignment_optional,
10
+ help_inline: if assignment_optional
11
+ _('Define the Resource Quota this host counts to.')
12
+ elsif !selected.nil? && (host_quota.nil? ||
13
+ host_quota == ForemanResourceQuota::ResourceQuota.unassigned.id)
14
+ format(_("Quota required! Choosing '%s' by default, change here if needed!"),
15
+ user_quotas.find(selected))
16
+ else
17
+ _('Resource quota assignment required!')
18
+ end }
19
+
8
20
  select_f form,
9
21
  :resource_quota_id,
10
- select_items,
22
+ user_quotas,
11
23
  :id,
12
24
  :to_label,
13
- blank_opt,
14
- label: _('Resource Quota'),
15
- help_inline: _('Define the Resource Quota this host counts to.')
25
+ select_opts,
26
+ html_opts
16
27
  end
17
28
  end
18
29
  end
@@ -8,5 +8,6 @@ module ForemanResourceQuota
8
8
  class HostResourcesException < ResourceQuotaException; end
9
9
  class ResourceQuotaUtilizationException < ResourceQuotaException; end
10
10
  class HostNotFoundException < ResourceQuotaException; end
11
+ class UnassignedQuotaDeletionException < ResourceQuotaException; end
11
12
  end
12
13
  end
@@ -128,22 +128,21 @@ module ForemanResourceQuota
128
128
  "while processing host '#{name}'. The Resource Quota utilization values might be inconsistent.")
129
129
  end
130
130
 
131
- def early_return?(quota)
132
- if quota.nil?
133
- return true if quota_assigment_optional?
134
- raise HostResourceQuotaEmptyException, 'must be given.'
131
+ def early_return?(quota) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
132
+ if quota.nil? || quota.unassigned?
133
+ self.resource_quota = ResourceQuota.unassigned
134
+ return true if owner.quota_assignment_optional?
135
+ if ResourceQuota.assignable.empty? || (!owner.admin? && owner.resource_quotas.assignable.empty?)
136
+ raise HostResourceQuotaEmptyException,
137
+ 'must be given.'
138
+ end
139
+ return true
135
140
  end
136
141
  return true if quota.active_resources.empty?
137
142
  return true if Setting[:resource_quota_global_no_action] # quota is assigned, but not supposed to be checked
138
143
  false
139
144
  end
140
145
 
141
- def quota_assigment_optional?
142
- return true if Setting[:resource_quota_optional_assignment]
143
- return true if owner.respond_to?(:resource_quota_is_optional) && owner.resource_quota_is_optional
144
- false
145
- end
146
-
147
146
  def save_host_resources
148
147
  host_resources.save
149
148
  end
@@ -4,12 +4,39 @@ module ForemanResourceQuota
4
4
  module UserExtensions
5
5
  extend ActiveSupport::Concern
6
6
  included do
7
+ after_create :set_unassigned_quota
8
+
7
9
  has_many :resource_quotas_users, class_name: 'ForemanResourceQuota::ResourceQuotaUser', dependent: :destroy,
8
10
  inverse_of: :user
9
11
  has_many :resource_quotas, class_name: 'ForemanResourceQuota::ResourceQuota', through: :resource_quotas_users
10
12
  attribute :resource_quota_is_optional, :boolean, default: false
11
13
 
12
14
  scoped_search relation: :resource_quotas, on: :name, complete_value: true, rename: :resource_quota
15
+
16
+ def assignable_resource_quotas
17
+ resource_quotas.assignable
18
+ end
19
+
20
+ def quota_assignment_optional?
21
+ Setting['resource_quota_optional_assignment'] || resource_quota_is_optional
22
+ end
23
+
24
+ def show_unassigned_hosts_warning?
25
+ return false if Setting['resource_quota_optional_assignment']
26
+ (admin? &&
27
+ !ForemanResourceQuota::ResourceQuota.unassigned.hosts.empty?) ||
28
+ (!admin? &&
29
+ !resource_quota_is_optional &&
30
+ !ForemanResourceQuota::ResourceQuota.unassigned.hosts.where(owner: self).empty?)
31
+ end
32
+
33
+ private
34
+
35
+ def set_unassigned_quota
36
+ return if resource_quotas.include?(ForemanResourceQuota::ResourceQuota.unassigned)
37
+
38
+ resource_quotas << ForemanResourceQuota::ResourceQuota.unassigned
39
+ end
13
40
  end
14
41
  end
15
42
  end
@@ -4,11 +4,29 @@ module ForemanResourceQuota
4
4
  module UsergroupExtensions
5
5
  extend ActiveSupport::Concern
6
6
  included do
7
+ after_create :set_unassigned_quota
8
+
7
9
  has_many :resource_quotas_usergroups, class_name: 'ForemanResourceQuota::ResourceQuotaUsergroup',
8
10
  dependent: :destroy, inverse_of: :usergroup
9
11
  has_many :resource_quotas, class_name: 'ForemanResourceQuota::ResourceQuota', through: :resource_quotas_usergroups
10
12
 
11
13
  scoped_search relation: :resource_quotas, on: :name, complete_value: true, rename: :resource_quota
14
+
15
+ def assignable_resource_quotas
16
+ resource_quotas.assignable
17
+ end
18
+
19
+ def quota_assignment_optional?
20
+ Setting['resource_quota_optional_assignment']
21
+ end
22
+
23
+ private
24
+
25
+ def set_unassigned_quota
26
+ return if resource_quotas.include?(ForemanResourceQuota::ResourceQuota.unassigned)
27
+
28
+ resource_quotas << ForemanResourceQuota::ResourceQuota.unassigned
29
+ end
12
30
  end
13
31
  end
14
32
  end
@@ -23,13 +23,26 @@ module ForemanResourceQuota
23
23
 
24
24
  validates :name, presence: true, uniqueness: true
25
25
 
26
+ before_destroy :do_not_destroy_unassigned_quota, prepend: true
27
+ before_destroy :assign_unassigned_quota, prepend: true
28
+
26
29
  scoped_search on: :name, complete_value: true
27
30
  scoped_search on: :id, complete_enabled: false, only_explicit: true, validator: ScopedSearch::Validators::INTEGER
28
31
 
32
+ scope :assignable, -> { where(unassigned: false) }
33
+
34
+ def self.unassigned
35
+ find_by(unassigned: true)
36
+ end
37
+
29
38
  def self.permission_name
30
39
  'resource_quotas'
31
40
  end
32
41
 
42
+ def unassigned?
43
+ unassigned
44
+ end
45
+
33
46
  def number_of_hosts
34
47
  hosts_resources.size
35
48
  end
@@ -151,6 +164,16 @@ module ForemanResourceQuota
151
164
 
152
165
  private
153
166
 
167
+ def do_not_destroy_unassigned_quota
168
+ return unless id == ResourceQuota.unassigned.id
169
+ raise UnassignedQuotaDeletionException,
170
+ "You cannot delete the 'Unassigned' quota."
171
+ end
172
+
173
+ def assign_unassigned_quota
174
+ hosts.update(resource_quota: ResourceQuota.unassigned)
175
+ end
176
+
154
177
  # Wrap into a function for easier testing
155
178
  def call_utilization_helper(quota_hosts)
156
179
  utilization_from_resource_origins(active_resources, quota_hosts)
@@ -6,6 +6,9 @@
6
6
  <% end %>
7
7
 
8
8
  <% title _('Resource Quotas') %>
9
+ <% if User.current.show_unassigned_hosts_warning? %>
10
+ <%= alert :class => 'alert-warning', :header => _('You have unassigned hosts!'), text: "The setting 'resource_quota_optional_assignment' is set to 'No' but there are hosts without quota assignment. Please check your hosts' quota assignments! " %>
11
+ <% end %>
9
12
 
10
13
  <%= title_actions react_component('CreateResourceQuotaModal') %>
11
14
 
@@ -22,8 +25,10 @@
22
25
  </thead>
23
26
  <tbody>
24
27
  <% @resource_quotas.each do |quota|
28
+ showAssignmentWarning = quota.unassigned? && User.current.show_unassigned_hosts_warning?
25
29
  react_data = {
26
30
  "isNewQuota": false,
31
+ "showAssignmentWarning": showAssignmentWarning,
27
32
  "initialProperties": {
28
33
  "id": quota.id,
29
34
  "name": quota.name,
@@ -31,6 +36,7 @@
31
36
  "cpu_cores": quota.cpu_cores,
32
37
  "memory_mb": quota.memory_mb,
33
38
  "disk_gb": quota.disk_gb,
39
+ "unassigned": quota.unassigned?,
34
40
  },
35
41
  }
36
42
  %>
@@ -43,9 +49,11 @@
43
49
  <td><%= h(quota.memory_mb) %></td>
44
50
  <td><%= h(quota.disk_gb) %></td>
45
51
  <td>
52
+ <% unless quota.unassigned? %>
46
53
  <%= action_buttons(
47
54
  display_delete_if_authorized(hash_for_foreman_resource_quota_resource_quota_path(id: quota), data: { confirm: _("Delete %s?") % quota.name})
48
- ) %>
55
+ ) %>
56
+ <% end %>
49
57
  </td>
50
58
  </tr>
51
59
  <% end %>
@@ -1,4 +1,16 @@
1
1
  <%
2
- user_quotas = User.current&.admin? ? ForemanResourceQuota::ResourceQuota.all : User.current.resource_quotas
2
+ user_quotas = User.current&.admin? ? ForemanResourceQuota::ResourceQuota.all : User.current.resource_quotas.distinct
3
+ user_quotas = user_quotas.assignable if !User.current.quota_assignment_optional?
4
+ user_quotas = user_quotas.order(:name)
5
+ selected = @host.resource_quota_id
6
+ # show Unassigned as default when assignment is optional
7
+ # show first selectable quota when assignment is mandatory
8
+ if selected.nil? || selected == ForemanResourceQuota::ResourceQuota.unassigned.id
9
+ if User.current.quota_assignment_optional?
10
+ selected = ForemanResourceQuota::ResourceQuota.unassigned.id
11
+ else
12
+ selected = user_quotas&.first&.id
13
+ end
14
+ end
3
15
  %>
4
- <%= resource_quota_select form, user_quotas %>
16
+ <%= resource_quota_select form, user_quotas, selected, User.current.quota_assignment_optional?, @host.resource_quota_id %>
@@ -10,7 +10,7 @@
10
10
  :label_help => _("It is optional for a user to assign a quota when creating new hosts") %>
11
11
  <% end %>
12
12
 
13
- <%= multiple_checkboxes(form, :resource_quotas, resource, ForemanResourceQuota::ResourceQuota, :label => _("Resource Quotas")) %>
13
+ <%= multiple_checkboxes(form, :resource_quotas, resource, ForemanResourceQuota::ResourceQuota.assignable, :label => _("Resource Quotas")) %>
14
14
 
15
15
  <% if resource_type == :user %>
16
16
  <% usergroups = @user.cached_usergroups.includes(:resource_quotas).distinct %>
@@ -34,7 +34,7 @@
34
34
  <% unless usergroup.resource_quotas.map(&:name).any? %>
35
35
  <li data-id="<%= usergroup.id %>" class="list-group-item"><%= _('This group has no quotas') %></li>
36
36
  <%end %>
37
- <% usergroup.resource_quotas.map(&:name).each do |quota_name| %>
37
+ <% usergroup.assignable_resource_quotas.map(&:name).each do |quota_name| %>
38
38
  <li data-id="<%= usergroup.id %>" class="list-group-item"><%= quota_name %></li>
39
39
  <% end %>
40
40
  <% end %>
@@ -1,7 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DropMissingHosts < ActiveRecord::Migration[6.1]
4
- def up
5
- drop_table :resource_quotas_missing_hosts
4
+ def change
5
+ drop_table :resource_quotas_missing_hosts do |t|
6
+ t.references :resource_quota, null: false, foreign_key: { to_table: :resource_quotas }
7
+ t.references :missing_host, null: false, unique: true, foreign_key: { to_table: :hosts }
8
+ t.boolean :no_cpu_cores, default: false
9
+ t.boolean :no_memory_mb, default: false
10
+ t.boolean :no_disk_gb, default: false
11
+ t.timestamps
12
+ end
6
13
  end
7
14
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddUnassignedFlagToResourceQuota < ActiveRecord::Migration[6.1]
4
+ def change
5
+ add_column :resource_quotas, :unassigned, :bool, null: false, default: false
6
+ end
7
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Default Quota "Unassigned"
4
+ ForemanResourceQuota::ResourceQuota.without_auditing do # rubocop:disable Metrics/BlockLength
5
+ unassigned = ForemanResourceQuota::ResourceQuota.where(
6
+ name: 'Unassigned',
7
+ unassigned: true,
8
+ description: 'Here, you can see all hosts without a dedicated quota.'
9
+ ).first_or_create
10
+
11
+ # Add default quota to all users and usergroups
12
+ User.without_auditing do
13
+ User.all.except_hidden.each do |user|
14
+ unless user.resource_quotas.include?(unassigned)
15
+ user.resource_quotas << unassigned
16
+ user.save!(validate: false)
17
+ end
18
+ end
19
+ end
20
+
21
+ Usergroup.without_auditing do
22
+ Usergroup.all.each do |usergroup|
23
+ unless usergroup.resource_quotas.include?(unassigned)
24
+ usergroup.resource_quotas << unassigned
25
+ usergroup.save!(validate: false)
26
+ end
27
+ end
28
+ end
29
+
30
+ # Move all hosts without a quota to quota "Unassigned"
31
+ Host.without_auditing do
32
+ Host.all.each do |host|
33
+ host.update(resource_quota: unassigned) if host.resource_quota.nil?
34
+ end
35
+ end
36
+ end
@@ -105,5 +105,13 @@ Foreman::Plugin.register :foreman_resource_quota do
105
105
  # Future: Overwrite quota-specific "out of resource"-action and take no ..
106
106
  end
107
107
  end
108
+ extend_page 'hosts/_list' do |context|
109
+ context.with_profile :resource_quota, _('Resource Quota'), default: true do
110
+ add_pagelet :hosts_table_column_header, key: :resource_quota_id, label: s_('Resource Quota'),
111
+ sortable: true, width: '10%', class: 'hidden-xs'
112
+ add_pagelet :hosts_table_column_content, key: :resource_quota_id,
113
+ callback: ->(host) { host.resource_quota.name }, class: 'hidden-xs ellipsis'
114
+ end
115
+ end
108
116
  end
109
117
  # rubocop: enable Metrics/BlockLength
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ForemanResourceQuota
4
- VERSION = '0.4.0'
4
+ VERSION = '0.6.0'
5
5
  end
@@ -4,11 +4,10 @@ require 'rake/testtask'
4
4
 
5
5
  # Tasks
6
6
  namespace :foreman_resource_quota do
7
- namespace :example do
8
- desc 'Example Task'
9
- task task: :environment do
10
- # Task goes here
11
- end
7
+ desc 'EXPERIMENTAL: Revert all database migrations of this plugin, preparing plugin uninstall'
8
+ task revert_db_migrations: :environment do
9
+ plugin = Foreman::Plugin.find ForemanResourceQuota.name.underscore
10
+ ActiveRecord::MigrationContext.new(plugin.migrations_paths, ActiveRecord::SchemaMigration).down
12
11
  end
13
12
  end
14
13
 
data/package.json CHANGED
@@ -24,20 +24,19 @@
24
24
  "@theforeman/vendor": ">= 12.0.1"
25
25
  },
26
26
  "devDependencies": {
27
- "@babel/core": "^7.23.0",
27
+ "@babel/core": "^7.28.0",
28
28
  "@sheerun/mutationobserver-shim": "^0.3.3",
29
- "@testing-library/react": "^16.2.0",
29
+ "@testing-library/react": "^16.3.0",
30
30
  "@testing-library/user-event": "^14.6.1",
31
- "@theforeman/builder": ">= 12.0.1",
32
- "@theforeman/eslint-plugin-foreman": ">= 12.0.1",
33
- "@theforeman/find-foreman": ">= 12.0.1",
34
- "@theforeman/test": ">= 12.0.1",
35
- "@theforeman/vendor-dev": ">= 12.0.1",
31
+ "@theforeman/builder": ">= 15.0.0",
32
+ "@theforeman/eslint-plugin-foreman": ">= 15.0.0",
33
+ "@theforeman/find-foreman": ">= 15.0.0",
34
+ "@theforeman/test": ">= 15.0.0",
35
+ "@theforeman/vendor-dev": ">= 15.0.0",
36
36
  "babel-eslint": "^10.0.3",
37
37
  "eslint": "^6.7.2",
38
38
  "prettier": "^1.19.1",
39
- "react-redux-test-utils": "^0.2.0",
40
- "stylelint": "^16.15.0",
41
- "stylelint-config-standard": "^37.0.0"
39
+ "stylelint": "^16.21.1",
40
+ "stylelint-config-standard": "^38.0.0"
42
41
  }
43
42
  }
@@ -20,6 +20,7 @@ const CreateResourceQuotaModal = () => {
20
20
  <div>
21
21
  <Button
22
22
  id="foreman-resource-quota-create-modal-button"
23
+ ouiaId="foreman-resource-quota-create-modal-button"
23
24
  variant="primary"
24
25
  onClick={() => {
25
26
  setIsOpen(true);
@@ -7,6 +7,7 @@ exports[`ResourceQuotaEmptyState should render 1`] = `
7
7
  <Button
8
8
  id="foreman-resource-quota-welcome-create-modal-button"
9
9
  onClick={[Function]}
10
+ ouiaId="foreman-resource-quota-welcome-create-modal-button"
10
11
  variant="primary"
11
12
  >
12
13
  Create resource quota
@@ -42,6 +43,7 @@ exports[`ResourceQuotaEmptyState should render 1`] = `
42
43
  onClose={[Function]}
43
44
  ouiaId="foreman-resource-quota-create-modal"
44
45
  ouiaSafe={true}
46
+ position="default"
45
47
  showClose={true}
46
48
  title="Create resource quota"
47
49
  titleIconVariant={null}
@@ -56,6 +58,7 @@ exports[`ResourceQuotaEmptyState should render 1`] = `
56
58
  "disk_gb": null,
57
59
  "memory_mb": null,
58
60
  "name": "",
61
+ "unassigned": false,
59
62
  }
60
63
  }
61
64
  initialStatus={
@@ -74,6 +77,7 @@ exports[`ResourceQuotaEmptyState should render 1`] = `
74
77
  isNewQuota={true}
75
78
  onSubmit={[Function]}
76
79
  quotaChangesCallback={null}
80
+ showAssignmentWarning={false}
77
81
  />
78
82
  </Modal>
79
83
  </div>
@@ -21,6 +21,7 @@ const ResourceQuotaEmptyState = () => {
21
21
  const ActionButton = (
22
22
  <Button
23
23
  id="foreman-resource-quota-welcome-create-modal-button"
24
+ ouiaId="foreman-resource-quota-welcome-create-modal-button"
24
25
  variant="primary"
25
26
  onClick={() => {
26
27
  setIsOpen(true);
@@ -1 +1 @@
1
- @import '~@theforeman/vendor/scss/variables';
1
+ @import 'foremanReact/common/variables';
@@ -2,6 +2,7 @@
2
2
  export const RESOURCE_IDENTIFIER_ID = 'id';
3
3
  export const RESOURCE_IDENTIFIER_NAME = 'name';
4
4
  export const RESOURCE_IDENTIFIER_DESCRIPTION = 'description';
5
+ export const RESOURCE_IDENTIFIER_UNASSIGNED = 'unassigned';
5
6
  export const RESOURCE_IDENTIFIER_CPU = 'cpu_cores';
6
7
  export const RESOURCE_IDENTIFIER_MEMORY = 'memory_mb';
7
8
  export const RESOURCE_IDENTIFIER_DISK = 'disk_gb';
@@ -1,9 +1,10 @@
1
- @import '~@theforeman/vendor/scss/variables';
1
+ @import 'foremanReact/common/variables';
2
2
 
3
- .pf-c-card__body {
3
+
4
+ .pf-v5-c-card__body {
4
5
  padding-top: 1rem;
5
6
  }
6
7
 
7
- .pf-c-content dl {
8
+ .pf-v5-c-content dl {
8
9
  margin-bottom: 0px;
9
10
  }
@@ -34,7 +34,7 @@ const StaticDetail = ({
34
34
  {isTextArea ? (
35
35
  <TextArea
36
36
  id={id}
37
- onChange={onChange}
37
+ onChange={(_event, val) => onChange(val)}
38
38
  value={value}
39
39
  validated={validated}
40
40
  isRequired={isRequired}
@@ -42,7 +42,8 @@ const StaticDetail = ({
42
42
  ) : (
43
43
  <TextInput
44
44
  id={id}
45
- onChange={onChange}
45
+ ouiaId={id}
46
+ onChange={(_event, val) => onChange(val)}
46
47
  value={value}
47
48
  validated={validated}
48
49
  isRequired={isRequired}
@@ -19,6 +19,7 @@ const TextInputField = ({
19
19
  isRequired,
20
20
  isTextArea,
21
21
  isNewQuota,
22
+ isDisabled,
22
23
  }) => {
23
24
  const dispatch = useDispatch();
24
25
  const [currentAttribute, setCurrentAttribute] = useState();
@@ -100,7 +101,7 @@ const TextInputField = ({
100
101
  loading={isLoading && currentAttribute === attribute}
101
102
  onEdit={onEdit}
102
103
  value={value}
103
- disabled={false}
104
+ disabled={isDisabled}
104
105
  textArea={isTextArea}
105
106
  validated={validated}
106
107
  {...{ currentAttribute, setCurrentAttribute }}
@@ -114,6 +115,7 @@ TextInputField.defaultProps = {
114
115
  isRequired: false,
115
116
  isRestrictInputValidation: false,
116
117
  isNewQuota: false,
118
+ isDisabled: false,
117
119
  };
118
120
 
119
121
  TextInputField.propTypes = {
@@ -127,6 +129,7 @@ TextInputField.propTypes = {
127
129
  isTextArea: PropTypes.bool,
128
130
  isRequired: PropTypes.bool,
129
131
  isNewQuota: PropTypes.bool,
132
+ isDisabled: PropTypes.bool,
130
133
  };
131
134
 
132
135
  export default TextInputField;