Skip to content
Draft
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
11 changes: 11 additions & 0 deletions app/controllers/v3/spaces_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'presenters/v3/paginated_list_presenter'
require 'presenters/v3/space_presenter'
require 'presenters/v3/space_usage_summary_presenter'
require 'presenters/v3/effective_space_quota_presenter'
require 'messages/space_create_message'
require 'messages/space_delete_unmapped_routes_message'
require 'messages/space_update_message'
Expand Down Expand Up @@ -220,6 +221,16 @@ def show_usage_summary
render status: :ok, json: Presenters::V3::SpaceUsageSummaryPresenter.new(space)
end

def show_effective_quota
space = fetch_space(hashed_params[:guid])
space_not_found! unless space
space_not_found! unless permission_queryer.can_read_from_space?(space.id, space.organization_id)

effective_quota = VCAP::CloudController::EffectiveSpaceQuotaCalculator.calculate(space)

render status: :ok, json: Presenters::V3::EffectiveSpaceQuotaPresenter.new(effective_quota, space)
end

private

def fetch_organization(guid)
Expand Down
64 changes: 64 additions & 0 deletions app/presenters/helpers/quota_presenter_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
module VCAP::CloudController::Presenters
class QuotaPresenterBuilder
def initialize(quota)
@quota = quota
@hash = {}
end

def build
@hash
end

def add_resource_limits
@hash.merge!({
apps: {
total_memory_in_mb: unlimited_to_nil(@quota.memory_limit),
per_process_memory_in_mb: unlimited_to_nil(@quota.instance_memory_limit),
total_instances: unlimited_to_nil(@quota.app_instance_limit),
per_app_tasks: unlimited_to_nil(@quota.app_task_limit),
log_rate_limit_in_bytes_per_second: unlimited_to_nil(@quota.log_rate_limit)
},
services: {
paid_services_allowed: @quota.non_basic_services_allowed,
total_service_instances: unlimited_to_nil(@quota.total_services),
total_service_keys: unlimited_to_nil(@quota.total_service_keys)
},
routes: {
total_routes: unlimited_to_nil(@quota.total_routes),
total_reserved_ports: unlimited_to_nil(@quota.total_reserved_route_ports)
}
})

if @quota.respond_to?(:guid)
@hash[:guid] = @quota.guid
@hash[:created_at] = @quota.created_at
@hash[:updated_at] = @quota.updated_at
@hash[:name] = @quota.name
end
self
end

def add_domains
@hash[:domains] = {
total_domains: unlimited_to_nil(@quota.total_private_domains)
}
self
end

def add_relationships(relationships)
@hash[:relationships] = relationships
self
end

def add_links(links)
@hash[:links] = links
self
end

private

def unlimited_to_nil(value)
value == -1 ? nil : value
end
end
end
32 changes: 32 additions & 0 deletions app/presenters/v3/effective_space_quota_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require 'presenters/v3/base_presenter'
require 'presenters/helpers/quota_presenter_builder'

module VCAP::CloudController::Presenters::V3
class EffectiveSpaceQuotaPresenter < BasePresenter
def initialize(effective_space_quota, space)
super(effective_space_quota)
@space = space
end

def to_hash
builder = VCAP::CloudController::Presenters::QuotaPresenterBuilder.new(effective_space_quota)
builder.add_resource_limits
builder.add_links(build_links)
builder.build
end

private

def build_links
{
self: { href: url_builder.build_url(path: "/v3/spaces/#{@space.guid}/effective_quota") },
usage_summary: { href: url_builder.build_url(path: "/v3/spaces/#{@space.guid}/usage_summary") },
space: { href: url_builder.build_url(path: "/v3/spaces/#{@space.guid}") }
}
end

def effective_space_quota
@resource
end
end
end
64 changes: 24 additions & 40 deletions app/presenters/v3/organization_quota_presenter.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'presenters/v3/base_presenter'
require 'presenters/mixins/metadata_presentation_helpers'
require 'presenters/helpers/quota_presenter_builder'

module VCAP::CloudController::Presenters::V3
class OrganizationQuotaPresenter < BasePresenter
Expand All @@ -16,59 +17,42 @@ def initialize(
end

def to_hash
{
guid: organization_quota.guid,
created_at: organization_quota.created_at,
updated_at: organization_quota.updated_at,
name: organization_quota.name,
apps: {
total_memory_in_mb: convert_unlimited_to_nil(organization_quota.memory_limit),
per_process_memory_in_mb: convert_unlimited_to_nil(organization_quota.instance_memory_limit),
total_instances: convert_unlimited_to_nil(organization_quota.app_instance_limit),
per_app_tasks: convert_unlimited_to_nil(organization_quota.app_task_limit),
log_rate_limit_in_bytes_per_second: convert_unlimited_to_nil(organization_quota.log_rate_limit)
},
services: {
paid_services_allowed: organization_quota.non_basic_services_allowed,
total_service_instances: convert_unlimited_to_nil(organization_quota.total_services),
total_service_keys: convert_unlimited_to_nil(organization_quota.total_service_keys)
},
routes: {
total_routes: convert_unlimited_to_nil(organization_quota.total_routes),
total_reserved_ports: convert_unlimited_to_nil(organization_quota.total_reserved_route_ports)
},
domains: {
total_domains: convert_unlimited_to_nil(organization_quota.total_private_domains)
},
relationships: {
organizations: {
data: filtered_visible_orgs
}
},
links: build_links
}
builder = VCAP::CloudController::Presenters::QuotaPresenterBuilder.new(quota)
builder.add_resource_limits.
add_domains.
add_relationships(relationships).
add_links(build_links)
builder.build
end

private

def filtered_visible_orgs
ds = organization_quota.organizations_dataset
ds = ds.where(guid: @visible_org_guids_query) unless @all_orgs_visible
ds.select_map(:guid).map { |g| { guid: g } }
def quota
@resource
end

def organization_quota
@resource
def relationships
{
organizations: {
data: filtered_visible_orgs
}
}
end

def convert_unlimited_to_nil(value)
value == -1 ? nil : value
def filtered_visible_orgs
ds = quota.organizations_dataset
ds = ds.where(guid: @visible_org_guids_query) unless @all_orgs_visible
ds.select_map(:guid).map { |g| { guid: g } }
end

def build_links
{
self: { href: url_builder.build_url(path: "/v3/organization_quotas/#{organization_quota.guid}") }
self: { href: url_builder.build_url(path: "/v3/organization_quotas/#{quota.guid}") }
}
end

def unlimited_to_nil(value)
value == -1 ? nil : value
end
end
end
62 changes: 22 additions & 40 deletions app/presenters/v3/space_quota_presenter.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'presenters/v3/base_presenter'
require 'presenters/mixins/metadata_presentation_helpers'
require 'presenters/helpers/quota_presenter_builder'

module VCAP::CloudController::Presenters::V3
class SpaceQuotaPresenter < BasePresenter
Expand All @@ -16,63 +17,44 @@ def initialize(
end

def to_hash
{
guid: space_quota.guid,
created_at: space_quota.created_at,
updated_at: space_quota.updated_at,
name: space_quota.name,
apps: {
total_memory_in_mb: unlimited_to_nil(space_quota.memory_limit),
per_process_memory_in_mb: unlimited_to_nil(space_quota.instance_memory_limit),
total_instances: unlimited_to_nil(space_quota.app_instance_limit),
per_app_tasks: unlimited_to_nil(space_quota.app_task_limit),
log_rate_limit_in_bytes_per_second: unlimited_to_nil(space_quota.log_rate_limit)
},
services: {
paid_services_allowed: space_quota.non_basic_services_allowed,
total_service_instances: unlimited_to_nil(space_quota.total_services),
total_service_keys: unlimited_to_nil(space_quota.total_service_keys)
},
routes: {
total_routes: unlimited_to_nil(space_quota.total_routes),
total_reserved_ports: unlimited_to_nil(space_quota.total_reserved_route_ports)
},
relationships: {
organization: {
data: { guid: space_quota.organization.guid }
},
spaces: {
data: filtered_visible_spaces
}
},
links: build_links
}
builder = VCAP::CloudController::Presenters::QuotaPresenterBuilder.new(quota)
builder.add_resource_limits.
add_relationships(relationships).
add_links(build_links)
builder.build
end

private

def space_quota
def quota
@resource
end

def relationships
{
organization: {
data: { guid: quota.organization.guid }
},
spaces: {
data: filtered_visible_spaces
}
}
end

def filtered_visible_spaces
visible_spaces = if @all_spaces_visible
space_quota.spaces
quota.spaces
else
space_quota.spaces.select { |space| @visible_space_guids.include? space.guid }
quota.spaces.select { |space| @visible_space_guids.include? space.guid }
end
visible_spaces.map { |space| { guid: space.guid } }
end

def build_links
{
self: { href: url_builder.build_url(path: "/v3/space_quotas/#{space_quota.guid}") },
organization: { href: url_builder.build_url(path: "/v3/organizations/#{space_quota.organization.guid}") }
self: { href: url_builder.build_url(path: "/v3/space_quotas/#{quota.guid}") },
organization: { href: url_builder.build_url(path: "/v3/organizations/#{quota.organization.guid}") }
}
end

def unlimited_to_nil(value)
value == -1 ? nil : value
end
end
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@
delete 'spaces/:guid', to: 'spaces_v3#destroy'
delete 'spaces/:guid/routes', to: 'spaces_v3#delete_unmapped_routes'
get '/spaces/:guid/usage_summary', to: 'spaces_v3#show_usage_summary'
get 'spaces/:guid/effective_quota', to: 'spaces_v3#show_effective_quota'
get '/spaces/:guid/relationships/isolation_segment', to: 'spaces_v3#show_isolation_segment'
patch '/spaces/:guid/relationships/isolation_segment', to: 'spaces_v3#update_isolation_segment'
get '/spaces/:guid/users', to: 'spaces_v3#list_members'
Expand Down
35 changes: 34 additions & 1 deletion docs/v3/source/includes/api_resources/_spaces.erb
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,42 @@
"self": {
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479/usage_summary"
},
"organization": {
"space": {
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
}
<% end %>

<% content_for :effective_space_quota do %>
{
"apps": {
"total_memory_in_mb": 10240,
"total_instances": 100,
"total_routes": 2000,
"total_service_instances": 1000,
"paid_service_plans_allowed": true
},
"services": {
"total_service_instances": 1000,
"paid_service_plans_allowed": true
},
"routes": {
"total_routes": 2000
},
"domains": {
"total_private_domains": 10
},
"links": {
"self": {
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479/effective_quota"
},
"usage_summary": {
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479/usage_summary"
},
"space": {
"href": "https://api.example.org/v3/spaces/f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
}
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
### Get effective space quota (experimental)

```
Example Request
```

```shell
curl "https://api.example.org/v3/spaces/[guid]/effective_quota" \
-X GET \
-H "Authorization: bearer [token]"
```

```
Example Response
```

```http
HTTP/1.1 200 OK
Content-Type: application/json

<%= yield_content :effective_space_quota, '/v3/spaces/:guid/effective_quota' %>
```

This endpoint retrieves the effective quota for the specified space to show the actual applicable resource limits.
The effective quota is dynamically calculated based on the space's and its organization's assigned quotas.

#### Definition
`GET /v3/spaces/:guid/effective_quota`

#### Permitted roles
|
--- |
Admin |
Admin Read-Only |
Global Auditor |
Org Manager |
Space Auditor |
Space Developer |
Space Manager |
Space Supporter |
Loading