Skip to content

Commit af484bb

Browse files
committed
Draft: Implement effective space quota endpoint
1 parent 068ac1a commit af484bb

16 files changed

+463
-109
lines changed

app/controllers/v3/spaces_controller.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'presenters/v3/paginated_list_presenter'
22
require 'presenters/v3/space_presenter'
33
require 'presenters/v3/space_usage_summary_presenter'
4+
require 'presenters/v3/effective_space_quota_presenter'
45
require 'messages/space_create_message'
56
require 'messages/space_delete_unmapped_routes_message'
67
require 'messages/space_update_message'
@@ -220,6 +221,16 @@ def show_usage_summary
220221
render status: :ok, json: Presenters::V3::SpaceUsageSummaryPresenter.new(space)
221222
end
222223

224+
def show_effective_quota
225+
space = fetch_space(hashed_params[:guid])
226+
space_not_found! unless space
227+
space_not_found! unless permission_queryer.can_read_from_space?(space.id, space.organization_id)
228+
229+
effective_quota = VCAP::CloudController::EffectiveSpaceQuotaCalculator.calculate(space)
230+
231+
render status: :ok, json: Presenters::V3::EffectiveSpaceQuotaPresenter.new(effective_quota, space)
232+
end
233+
223234
private
224235

225236
def fetch_organization(guid)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
module VCAP::CloudController::Presenters
2+
class QuotaPresenterBuilder
3+
def initialize(quota)
4+
@quota = quota
5+
@hash = {}
6+
end
7+
8+
def build
9+
@hash
10+
end
11+
12+
def add_resource_limits
13+
@hash.merge!({
14+
apps: {
15+
total_memory_in_mb: unlimited_to_nil(@quota.memory_limit),
16+
per_process_memory_in_mb: unlimited_to_nil(@quota.instance_memory_limit),
17+
total_instances: unlimited_to_nil(@quota.app_instance_limit),
18+
per_app_tasks: unlimited_to_nil(@quota.app_task_limit),
19+
log_rate_limit_in_bytes_per_second: unlimited_to_nil(@quota.log_rate_limit)
20+
},
21+
services: {
22+
paid_services_allowed: @quota.non_basic_services_allowed,
23+
total_service_instances: unlimited_to_nil(@quota.total_services),
24+
total_service_keys: unlimited_to_nil(@quota.total_service_keys)
25+
},
26+
routes: {
27+
total_routes: unlimited_to_nil(@quota.total_routes),
28+
total_reserved_ports: unlimited_to_nil(@quota.total_reserved_route_ports)
29+
}
30+
})
31+
32+
if @quota.respond_to?(:guid)
33+
@hash[:guid] = @quota.guid
34+
@hash[:created_at] = @quota.created_at
35+
@hash[:updated_at] = @quota.updated_at
36+
@hash[:name] = @quota.name
37+
end
38+
self
39+
end
40+
41+
def add_domains
42+
@hash[:domains] = {
43+
total_domains: unlimited_to_nil(@quota.total_private_domains)
44+
}
45+
self
46+
end
47+
48+
def add_relationships(relationships)
49+
@hash[:relationships] = relationships
50+
self
51+
end
52+
53+
def add_links(links)
54+
@hash[:links] = links
55+
self
56+
end
57+
58+
private
59+
60+
def unlimited_to_nil(value)
61+
value == -1 ? nil : value
62+
end
63+
end
64+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require 'presenters/v3/base_presenter'
2+
require 'presenters/helpers/quota_presenter_builder'
3+
4+
module VCAP::CloudController::Presenters::V3
5+
class EffectiveSpaceQuotaPresenter < BasePresenter
6+
def initialize(effective_space_quota, space)
7+
super(effective_space_quota)
8+
@space = space
9+
end
10+
11+
def to_hash
12+
builder = VCAP::CloudController::Presenters::QuotaPresenterBuilder.new(effective_space_quota)
13+
builder.add_resource_limits
14+
builder.add_links(build_links)
15+
builder.build
16+
end
17+
18+
private
19+
20+
def build_links
21+
{
22+
self: { href: url_builder.build_url(path: "/v3/spaces/#{@space.guid}/effective_quota") },
23+
usage_summary: { href: url_builder.build_url(path: "/v3/spaces/#{@space.guid}/usage_summary") },
24+
space: { href: url_builder.build_url(path: "/v3/spaces/#{@space.guid}") }
25+
}
26+
end
27+
28+
def effective_space_quota
29+
@resource
30+
end
31+
end
32+
end
Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'presenters/v3/base_presenter'
22
require 'presenters/mixins/metadata_presentation_helpers'
3+
require 'presenters/helpers/quota_presenter_builder'
34

45
module VCAP::CloudController::Presenters::V3
56
class OrganizationQuotaPresenter < BasePresenter
@@ -16,59 +17,42 @@ def initialize(
1617
end
1718

1819
def to_hash
19-
{
20-
guid: organization_quota.guid,
21-
created_at: organization_quota.created_at,
22-
updated_at: organization_quota.updated_at,
23-
name: organization_quota.name,
24-
apps: {
25-
total_memory_in_mb: convert_unlimited_to_nil(organization_quota.memory_limit),
26-
per_process_memory_in_mb: convert_unlimited_to_nil(organization_quota.instance_memory_limit),
27-
total_instances: convert_unlimited_to_nil(organization_quota.app_instance_limit),
28-
per_app_tasks: convert_unlimited_to_nil(organization_quota.app_task_limit),
29-
log_rate_limit_in_bytes_per_second: convert_unlimited_to_nil(organization_quota.log_rate_limit)
30-
},
31-
services: {
32-
paid_services_allowed: organization_quota.non_basic_services_allowed,
33-
total_service_instances: convert_unlimited_to_nil(organization_quota.total_services),
34-
total_service_keys: convert_unlimited_to_nil(organization_quota.total_service_keys)
35-
},
36-
routes: {
37-
total_routes: convert_unlimited_to_nil(organization_quota.total_routes),
38-
total_reserved_ports: convert_unlimited_to_nil(organization_quota.total_reserved_route_ports)
39-
},
40-
domains: {
41-
total_domains: convert_unlimited_to_nil(organization_quota.total_private_domains)
42-
},
43-
relationships: {
44-
organizations: {
45-
data: filtered_visible_orgs
46-
}
47-
},
48-
links: build_links
49-
}
20+
builder = VCAP::CloudController::Presenters::QuotaPresenterBuilder.new(quota)
21+
builder.add_resource_limits.
22+
add_domains.
23+
add_relationships(relationships).
24+
add_links(build_links)
25+
builder.build
5026
end
5127

5228
private
5329

54-
def filtered_visible_orgs
55-
ds = organization_quota.organizations_dataset
56-
ds = ds.where(guid: @visible_org_guids_query) unless @all_orgs_visible
57-
ds.select_map(:guid).map { |g| { guid: g } }
30+
def quota
31+
@resource
5832
end
5933

60-
def organization_quota
61-
@resource
34+
def relationships
35+
{
36+
organizations: {
37+
data: filtered_visible_orgs
38+
}
39+
}
6240
end
6341

64-
def convert_unlimited_to_nil(value)
65-
value == -1 ? nil : value
42+
def filtered_visible_orgs
43+
ds = quota.organizations_dataset
44+
ds = ds.where(guid: @visible_org_guids_query) unless @all_orgs_visible
45+
ds.select_map(:guid).map { |g| { guid: g } }
6646
end
6747

6848
def build_links
6949
{
70-
self: { href: url_builder.build_url(path: "/v3/organization_quotas/#{organization_quota.guid}") }
50+
self: { href: url_builder.build_url(path: "/v3/organization_quotas/#{quota.guid}") }
7151
}
7252
end
53+
54+
def unlimited_to_nil(value)
55+
value == -1 ? nil : value
56+
end
7357
end
7458
end
Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'presenters/v3/base_presenter'
22
require 'presenters/mixins/metadata_presentation_helpers'
3+
require 'presenters/helpers/quota_presenter_builder'
34

45
module VCAP::CloudController::Presenters::V3
56
class SpaceQuotaPresenter < BasePresenter
@@ -16,63 +17,44 @@ def initialize(
1617
end
1718

1819
def to_hash
19-
{
20-
guid: space_quota.guid,
21-
created_at: space_quota.created_at,
22-
updated_at: space_quota.updated_at,
23-
name: space_quota.name,
24-
apps: {
25-
total_memory_in_mb: unlimited_to_nil(space_quota.memory_limit),
26-
per_process_memory_in_mb: unlimited_to_nil(space_quota.instance_memory_limit),
27-
total_instances: unlimited_to_nil(space_quota.app_instance_limit),
28-
per_app_tasks: unlimited_to_nil(space_quota.app_task_limit),
29-
log_rate_limit_in_bytes_per_second: unlimited_to_nil(space_quota.log_rate_limit)
30-
},
31-
services: {
32-
paid_services_allowed: space_quota.non_basic_services_allowed,
33-
total_service_instances: unlimited_to_nil(space_quota.total_services),
34-
total_service_keys: unlimited_to_nil(space_quota.total_service_keys)
35-
},
36-
routes: {
37-
total_routes: unlimited_to_nil(space_quota.total_routes),
38-
total_reserved_ports: unlimited_to_nil(space_quota.total_reserved_route_ports)
39-
},
40-
relationships: {
41-
organization: {
42-
data: { guid: space_quota.organization.guid }
43-
},
44-
spaces: {
45-
data: filtered_visible_spaces
46-
}
47-
},
48-
links: build_links
49-
}
20+
builder = VCAP::CloudController::Presenters::QuotaPresenterBuilder.new(quota)
21+
builder.add_resource_limits.
22+
add_relationships(relationships).
23+
add_links(build_links)
24+
builder.build
5025
end
5126

5227
private
5328

54-
def space_quota
29+
def quota
5530
@resource
5631
end
5732

33+
def relationships
34+
{
35+
organization: {
36+
data: { guid: quota.organization.guid }
37+
},
38+
spaces: {
39+
data: filtered_visible_spaces
40+
}
41+
}
42+
end
43+
5844
def filtered_visible_spaces
5945
visible_spaces = if @all_spaces_visible
60-
space_quota.spaces
46+
quota.spaces
6147
else
62-
space_quota.spaces.select { |space| @visible_space_guids.include? space.guid }
48+
quota.spaces.select { |space| @visible_space_guids.include? space.guid }
6349
end
6450
visible_spaces.map { |space| { guid: space.guid } }
6551
end
6652

6753
def build_links
6854
{
69-
self: { href: url_builder.build_url(path: "/v3/space_quotas/#{space_quota.guid}") },
70-
organization: { href: url_builder.build_url(path: "/v3/organizations/#{space_quota.organization.guid}") }
55+
self: { href: url_builder.build_url(path: "/v3/space_quotas/#{quota.guid}") },
56+
organization: { href: url_builder.build_url(path: "/v3/organizations/#{quota.organization.guid}") }
7157
}
7258
end
73-
74-
def unlimited_to_nil(value)
75-
value == -1 ? nil : value
76-
end
7759
end
7860
end

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@
277277
delete 'spaces/:guid', to: 'spaces_v3#destroy'
278278
delete 'spaces/:guid/routes', to: 'spaces_v3#delete_unmapped_routes'
279279
get '/spaces/:guid/usage_summary', to: 'spaces_v3#show_usage_summary'
280+
get 'spaces/:guid/effective_quota', to: 'spaces_v3#show_effective_quota'
280281
get '/spaces/:guid/relationships/isolation_segment', to: 'spaces_v3#show_isolation_segment'
281282
patch '/spaces/:guid/relationships/isolation_segment', to: 'spaces_v3#update_isolation_segment'
282283
get '/spaces/:guid/users', to: 'spaces_v3#list_members'

docs/v3/source/includes/api_resources/_spaces.erb

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,42 @@
162162
"self": {
163163
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479/usage_summary"
164164
},
165-
"organization": {
165+
"space": {
166166
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479"
167167
}
168168
}
169169
}
170170
<% end %>
171+
172+
<% content_for :effective_space_quota do %>
173+
{
174+
"apps": {
175+
"total_memory_in_mb": 10240,
176+
"total_instances": 100,
177+
"total_routes": 2000,
178+
"total_service_instances": 1000,
179+
"paid_service_plans_allowed": true
180+
},
181+
"services": {
182+
"total_service_instances": 1000,
183+
"paid_service_plans_allowed": true
184+
},
185+
"routes": {
186+
"total_routes": 2000
187+
},
188+
"domains": {
189+
"total_private_domains": 10
190+
},
191+
"links": {
192+
"self": {
193+
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479/effective_quota"
194+
},
195+
"usage_summary": {
196+
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479/usage_summary"
197+
},
198+
"space": {
199+
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479"
200+
}
201+
}
202+
}
203+
<% end %>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
### Get effective space quota (experimental)
2+
3+
```
4+
Example Request
5+
```
6+
7+
```shell
8+
curl "https://api.example.org/v3/spaces/[guid]/effective_quota" \
9+
-X GET \
10+
-H "Authorization: bearer [token]"
11+
```
12+
13+
```
14+
Example Response
15+
```
16+
17+
```http
18+
HTTP/1.1 200 OK
19+
Content-Type: application/json
20+
21+
<%= yield_content :effective_space_quota, '/v3/spaces/:guid/effective_quota' %>
22+
```
23+
24+
This endpoint retrieves the effective quota for the specified space to show the actual applicable resource limits.
25+
The effective quota is dynamically calculated based on the space's and its organization's assigned quotas.
26+
27+
#### Definition
28+
`GET /v3/spaces/:guid/effective_quota`
29+
30+
#### Permitted roles
31+
|
32+
--- |
33+
Admin |
34+
Admin Read-Only |
35+
Global Auditor |
36+
Org Manager |
37+
Space Auditor |
38+
Space Developer |
39+
Space Manager |
40+
Space Supporter |

0 commit comments

Comments
 (0)