Skip to content

Commit db40bdd

Browse files
committed
Implement /v3/spaces/#{space.guid}/usage_summary
1 parent 3635a6b commit db40bdd

File tree

4 files changed

+138
-5
lines changed

4 files changed

+138
-5
lines changed

app/controllers/v3/spaces_controller.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,14 @@ def list_members
211211
raise CloudController::Errors::ApiError.new_from_details('UaaUnavailable')
212212
end
213213

214+
def show_usage_summary
215+
space = fetch_space(hashed_params[:guid])
216+
space_not_found! unless space
217+
space_not_found! unless permission_queryer.can_read_from_space?(space.id, space.organization_id)
218+
219+
render status: :ok, json: Presenters::V3::SpaceUsageSummaryPresenter.new(org)
220+
end
221+
214222
private
215223

216224
def fetch_organization(guid)

app/models/runtime/space.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,14 @@ def find_visible_service_instance_by_name(name)
269269
(shared | source).first
270270
end
271271

272+
def number_service_instances
273+
service_instances_dataset.count
274+
end
275+
276+
def number_service_keys
277+
ServiceKey.where(service_instance_id: service_instances_dataset.select(:id)).count
278+
end
279+
272280
def self.user_visibility_filter(user)
273281
{
274282
spaces__id: user.space_developer_space_ids.
@@ -328,6 +336,14 @@ def members
328336
User.dataset.where(id: Role.where(space_id: id).distinct.select(:user_id))
329337
end
330338

339+
def memory_used
340+
started_app_memory + running_task_memory
341+
end
342+
343+
def running_and_pending_tasks_count
344+
tasks_dataset.where(state: [TaskModel::PENDING_STATE, TaskModel::RUNNING_STATE]).count
345+
end
346+
331347
private
332348

333349
def has_manager?(user)
@@ -339,7 +355,6 @@ def has_auditor?(user)
339355
end
340356

341357
def memory_remaining
342-
memory_used = started_app_memory + running_task_memory
343358
space_quota_definition.memory_limit - memory_used
344359
end
345360

@@ -363,10 +378,6 @@ def started_app_log_rate_limit
363378
processes_dataset.where(state: ProcessModel::STARTED).sum(Sequel.*(:log_rate_limit, :instances)) || 0
364379
end
365380

366-
def running_and_pending_tasks_count
367-
tasks_dataset.where(state: [TaskModel::PENDING_STATE, TaskModel::RUNNING_STATE]).count
368-
end
369-
370381
def validate_isolation_segment_set(isolation_segment_model)
371382
isolation_segment_guids = organization.isolation_segment_models.map(&:guid)
372383
return if isolation_segment_guids.include?(isolation_segment_model.guid)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
require 'presenters/v3/base_presenter'
2+
3+
module VCAP::CloudController::Presenters::V3
4+
class SpaceUsageSummaryPresenter < BasePresenter
5+
def to_hash
6+
{
7+
usage_summary: {
8+
started_instances: started_instances,
9+
memory_in_mb: space.memory_used,
10+
routes: space.routes_dataset.count,
11+
service_instances: space.number_service_instances,
12+
reserved_ports: VCAP::CloudController::SpaceReservedRoutePorts.new(space).count,
13+
domains: space.organization.owned_private_domains_dataset.count,
14+
per_app_tasks: space.running_and_pending_tasks_count,
15+
service_keys: space.number_service_keys
16+
},
17+
links: build_links
18+
}
19+
end
20+
21+
private
22+
23+
def space
24+
@resource
25+
end
26+
27+
def started_instances
28+
space.processes_dataset.where(state: VCAP::CloudController::ProcessModel::STARTED).sum(:instances) || 0
29+
end
30+
31+
def build_links
32+
{
33+
self: {
34+
href: url_builder.build_url(path: "/v3/spaces/#{space.guid}/usage_summary")
35+
},
36+
space: {
37+
href: url_builder.build_url(path: "/v3/spaces/#{space.guid}")
38+
}
39+
}
40+
end
41+
end
42+
end
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
require 'spec_helper'
2+
require 'presenters/v3/space_usage_summary_presenter'
3+
4+
module VCAP::CloudController::Presenters::V3
5+
RSpec.describe SpaceUsageSummaryPresenter do
6+
let(:org) { VCAP::CloudController::Organization.make }
7+
let(:space) { VCAP::CloudController::Space.make(organization: org) }
8+
9+
context 'empty space' do
10+
describe '#to_hash' do
11+
let(:result) { SpaceUsageSummaryPresenter.new(space).to_hash }
12+
13+
it 'presents the space usage summary as json' do
14+
expect(result[:usage_summary][:started_instances]).to eq(0)
15+
expect(result[:usage_summary][:memory_in_mb]).to eq(0)
16+
expect(result[:usage_summary][:routes]).to eq(0)
17+
expect(result[:usage_summary][:service_instances]).to eq(0)
18+
expect(result[:usage_summary][:reserved_ports]).to eq(0)
19+
expect(result[:usage_summary][:domains]).to eq(0)
20+
expect(result[:usage_summary][:per_app_tasks]).to eq(0)
21+
expect(result[:usage_summary][:service_keys]).to eq(0)
22+
23+
expect(result[:links][:self][:href]).to match(%r{/v3/spaces/#{space.guid}/usage_summary$})
24+
expect(result[:links][:space][:href]).to match(%r{/v3/spaces/#{space.guid}$})
25+
end
26+
end
27+
end
28+
29+
context 'space with instances, routes and services' do
30+
let(:app_model) { VCAP::CloudController::AppModel.make(name: 'App Model', space: space) }
31+
let(:process) { VCAP::CloudController::ProcessModel.make(:process, state: VCAP::CloudController::ProcessModel::STARTED, memory: 512, app: app_model) }
32+
let(:task) { VCAP::CloudController::TaskModel.make(app: app_model, state: VCAP::CloudController::TaskModel::RUNNING_STATE, memory_in_mb: 512) }
33+
let(:shared_domain) { VCAP::CloudController::SharedDomain.make(router_group_guid: '123') }
34+
let(:private_domain) { VCAP::CloudController::PrivateDomain.make(owning_organization: org) }
35+
let(:route) { VCAP::CloudController::Route.make(host: '', domain: shared_domain, space: space, port: 4444) }
36+
let(:broker) { VCAP::CloudController::ServiceBroker.make }
37+
let(:service) { VCAP::CloudController::Service.make(service_broker: broker) }
38+
let(:service_plan) { VCAP::CloudController::ServicePlan.make(service: service, public: true) }
39+
let(:service_instance) { VCAP::CloudController::ManagedServiceInstance.make(space:, service_plan:) }
40+
let(:service_key) { VCAP::CloudController::ServiceKey.make(service_instance:) }
41+
42+
describe '#to_hash' do
43+
let(:result) { SpaceUsageSummaryPresenter.new(space).to_hash }
44+
45+
it 'presents the space usage summary as json' do
46+
process # TODO: Ensure the process is created before the test runs
47+
task
48+
49+
router_group = double('router_group', type: 'tcp', reservable_ports: [4444])
50+
routing_api_client = double('routing_api_client', router_group: router_group, enabled?: true)
51+
allow(CloudController::DependencyLocator).to receive(:instance).and_return(double(:api_client, routing_api_client:))
52+
private_domain
53+
route
54+
service_instance
55+
service_key
56+
57+
expect(result[:usage_summary][:started_instances]).to eq(1)
58+
expect(result[:usage_summary][:memory_in_mb]).to eq(1024)
59+
expect(result[:usage_summary][:routes]).to eq(1)
60+
expect(result[:usage_summary][:service_instances]).to eq(1)
61+
expect(result[:usage_summary][:reserved_ports]).to eq(1)
62+
expect(result[:usage_summary][:domains]).to eq(1)
63+
expect(result[:usage_summary][:per_app_tasks]).to eq(1)
64+
expect(result[:usage_summary][:service_keys]).to eq(1)
65+
66+
expect(result[:links][:self][:href]).to match(%r{/v3/spaces/#{space.guid}/usage_summary$})
67+
expect(result[:links][:space][:href]).to match(%r{/v3/spaces/#{space.guid}$})
68+
end
69+
end
70+
end
71+
end
72+
end

0 commit comments

Comments
 (0)