Skip to content

Commit 1290b15

Browse files
philippthunSven Krieger
andcommitted
Implement GET /v3/processes/:guid/process_instances
This endpoint returns information for all process instances (= actual LRPs) for the given process. Per process instance the following information is shown: index, state, since. The data is retrieved from Diego via the endpoint '/v1/actual_lrps/list_by_process_guids'. The InstancesStatsReporter processes this data as follows: - group by process guid - sort / group by index - keep only the newest LRP per index - fill missing instances (i.e. less actual LRPs than desired) with DOWN state Additional changes: - BbsInstancesClient: handle_diego_errors needs check for ResourceNotFound only when calling desired_lrp_by_process_guid. The other methods return an empty list. - BuildpackPresenter, ProcessPresenter: remove useless method Co-authored-by: Sven Krieger <sven.krieger@sap.com>
1 parent 71e7e39 commit 1290b15

16 files changed

Lines changed: 463 additions & 36 deletions

File tree

.rubocop_todo.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,6 @@ Lint/UnusedMethodArgument:
8585
Lint/UselessMethodDefinition:
8686
Exclude:
8787
- 'app/messages/route_destination_update_message.rb'
88-
- 'app/presenters/v3/buildpack_presenter.rb'
89-
- 'app/presenters/v3/process_presenter.rb'
9088
- 'spec/support/fake_front_controller.rb'
9189

9290
# Offense count: 791

app/controllers/v3/processes_controller.rb

Lines changed: 7 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/process_presenter'
33
require 'presenters/v3/process_stats_presenter'
4+
require 'presenters/v3/process_instances_presenter'
45
require 'cloud_controller/paging/pagination_options'
56
require 'actions/process_delete'
67
require 'fetchers/process_list_fetcher'
@@ -106,6 +107,12 @@ def stats
106107
render status: :ok, json: Presenters::V3::ProcessStatsPresenter.new(@process.type, process_stats)
107108
end
108109

110+
def process_instances
111+
instances = instances_reporters.instances_for_processes([@process])
112+
113+
render status: :ok, json: Presenters::V3::ProcessInstancesPresenter.new(instances[@process.guid], @process)
114+
end
115+
109116
private
110117

111118
def find_process_and_space

app/presenters/v3/buildpack_presenter.rb

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@ def to_hash
2626
}
2727
end
2828

29-
class << self
30-
# :labels and :annotations come from MetadataPresentationHelpers
31-
def associated_resources
32-
super
33-
end
34-
end
35-
3629
private
3730

3831
def buildpack
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
require 'presenters/v3/base_presenter'
2+
require 'presenters/mixins/metadata_presentation_helpers'
3+
4+
module VCAP::CloudController
5+
module Presenters
6+
module V3
7+
class ProcessInstancesPresenter < BasePresenter
8+
attr_reader :process
9+
10+
def initialize(instances, process)
11+
super(instances)
12+
@process = process
13+
end
14+
15+
def to_hash
16+
{
17+
resources: build_instances,
18+
links: build_links
19+
}
20+
end
21+
22+
private
23+
24+
def instances
25+
@resource
26+
end
27+
28+
def build_instances
29+
instances.map do |index, instance|
30+
{
31+
index: index,
32+
state: instance[:state],
33+
since: instance[:since]
34+
}
35+
end
36+
end
37+
38+
def build_links
39+
{
40+
self: { href: url_builder.build_url(path: "/v3/processes/#{process.guid}/process_instances") },
41+
process: { href: url_builder.build_url(path: "/v3/processes/#{process.guid}") }
42+
}
43+
end
44+
end
45+
end
46+
end
47+
end

app/presenters/v3/process_presenter.rb

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,6 @@ module V3
88
class ProcessPresenter < BasePresenter
99
include VCAP::CloudController::Presenters::Mixins::MetadataPresentationHelpers
1010

11-
class << self
12-
# :labels and :annotations come from MetadataPresentationHelpers
13-
def associated_resources
14-
super
15-
end
16-
end
17-
1811
def to_hash
1912
health_check_data = { timeout: process.health_check_timeout, invocation_timeout: process.health_check_invocation_timeout, interval: process.health_check_interval }
2013
health_check_data[:endpoint] = process.health_check_http_endpoint if process.health_check_type == HealthCheckTypes::HTTP

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
get '/processes', to: 'processes#index'
5656
get '/processes/:process_guid', to: 'processes#show'
5757
patch '/processes/:process_guid', to: 'processes#update'
58+
get '/processes/:process_guid/process_instances', to: 'processes#process_instances'
5859
delete '/processes/:process_guid/instances/:index', to: 'processes#terminate'
5960
post '/processes/:process_guid/actions/scale', to: 'processes#scale'
6061
get '/processes/:process_guid/stats', to: 'processes#stats'

lib/cloud_controller/backends/instances_reporters.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def stats_for_app(app)
2828
end
2929

3030
delegate :number_of_starting_and_running_instances_for_processes, :instance_count_summary, to: :diego_reporter
31+
delegate :instances_for_processes, to: :diego_stats_reporter
3132

3233
private
3334

@@ -36,7 +37,7 @@ def diego_reporter
3637
end
3738

3839
def diego_stats_reporter
39-
Diego::InstancesStatsReporter.new(dependency_locator.bbs_instances_client, dependency_locator.log_cache_metrics_client)
40+
@diego_stats_reporter ||= Diego::InstancesStatsReporter.new(dependency_locator.bbs_instances_client, dependency_locator.log_cache_metrics_client)
4041
end
4142

4243
def dependency_locator

lib/cloud_controller/diego/bbs_instances_client.rb

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def lrp_instances(process)
1111
process_guid = ProcessGuid.from_process(process)
1212
logger.info('lrp.instances.request', process_guid:)
1313

14-
actual_lrps_response = handle_diego_errors(process_guid) do
14+
actual_lrps_response = handle_diego_errors do
1515
response = @client.actual_lrps_by_process_guid(process_guid)
1616
logger.info('lrp.instances.response', process_guid: process_guid, error: response.error)
1717
response
@@ -20,30 +20,42 @@ def lrp_instances(process)
2020
actual_lrps_response.actual_lrps
2121
end
2222

23+
def actual_lrps_by_processes(processes)
24+
process_guids = processes.map { |process| ProcessGuid.from_process(process) }
25+
logger.info('actual.lrps.by.processes.request', process_guids:)
26+
27+
actual_lrps_response = handle_diego_errors do
28+
response = @client.actual_lrps_by_process_guids(process_guids)
29+
logger.info('actual.lrps.by.processes.response', process_guids: process_guids, error: response.error)
30+
response
31+
end
32+
33+
actual_lrps_response.actual_lrps
34+
end
35+
2336
def desired_lrp_instance(process)
2437
process_guid = ProcessGuid.from_process(process)
25-
response = handle_diego_errors(process_guid) do
38+
response = handle_diego_errors(handle_resource_not_found: true, process_guid: process_guid) do
2639
@client.desired_lrp_by_process_guid(process_guid)
2740
end
2841
response.desired_lrp
2942
end
3043

3144
private
3245

33-
def handle_diego_errors(process_guid)
46+
def handle_diego_errors(handle_resource_not_found: false, process_guid: nil)
3447
begin
3548
response = yield
3649
rescue ::Diego::Error => e
3750
raise CloudController::Errors::InstancesUnavailable.new(e)
3851
end
3952

4053
if response.error
41-
if response.error.type == ::Diego::Bbs::ErrorTypes::ResourceNotFound
54+
if handle_resource_not_found && response.error.type == ::Diego::Bbs::ErrorTypes::ResourceNotFound
4255
raise CloudController::Errors::NoRunningInstances.new("No running instances found for process guid #{process_guid}")
4356
end
4457

4558
raise CloudController::Errors::InstancesUnavailable.new(response.error.message)
46-
4759
end
4860

4961
response

lib/cloud_controller/diego/reporters/instances_stats_reporter.rb

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,48 @@ def stats_for_app(process)
2929
raise exception
3030
end
3131

32+
def instances_for_processes(processes)
33+
logger.debug('instances_for_processes.fetching_actual_lrps')
34+
35+
# Fetch actual_lrps for all processes
36+
actual_lrps = bbs_instances_client.actual_lrps_by_processes(processes)
37+
38+
lrps_by_process_guid = actual_lrps.group_by { |lrp| (pg = lrp.actual_lrp_key&.process_guid) && ProcessGuid.cc_process_guid(pg) }
39+
40+
current_time_since_epoch_ns = Time.now.utc.to_f * 1e9
41+
results = {}
42+
processes.each do |process|
43+
newest_lrp_by_index = (lrps_by_process_guid[process.guid] || []).
44+
group_by { |lrp| lrp.actual_lrp_key&.index }.
45+
transform_values { |lrps| lrps.max_by { |lrp| lrp.since || 0 } }
46+
47+
instances = {}
48+
# Fill in the instances up to the max of desired instances and actual instances
49+
[process.instances, newest_lrp_by_index.length].max.times do |idx|
50+
lrp = newest_lrp_by_index[idx]
51+
instances[idx] = if lrp
52+
{
53+
state: LrpStateTranslator.translate_lrp_state(lrp),
54+
since: nanoseconds_to_seconds(current_time_since_epoch_ns - lrp.since)
55+
}
56+
else
57+
{ state: VCAP::CloudController::Diego::LRP_DOWN }
58+
end
59+
end
60+
61+
results[process.guid] = instances
62+
end
63+
64+
results
65+
end
66+
3267
private
3368

3469
attr_reader :bbs_instances_client
3570

3671
def get_stats(desired_lrp, process)
3772
log_cache_data, log_cache_errors = envelopes(desired_lrp, process)
38-
stats = formatted_process_stats(log_cache_data, Time.now.to_datetime.rfc3339)
73+
stats = formatted_process_stats(log_cache_data, Time.now.utc.to_datetime.rfc3339)
3974
quota_stats = formatted_quota_stats(log_cache_data)
4075
isolation_segment = desired_lrp.PlacementTags.first
4176
[log_cache_errors, stats, quota_stats, isolation_segment]
@@ -79,9 +114,9 @@ def build_info(state, actual_lrp, process, stats, quota_stats, log_cache_errors)
79114
instance_guid: actual_lrp.actual_lrp_instance_key.instance_guid,
80115
port: get_default_port(actual_lrp.actual_lrp_net_info),
81116
net_info: actual_lrp_net_info_to_hash(actual_lrp.actual_lrp_net_info),
82-
uptime: nanoseconds_to_seconds((Time.now.to_f * 1e9) - actual_lrp.since),
117+
uptime: nanoseconds_to_seconds((Time.now.utc.to_f * 1e9) - actual_lrp.since),
83118
fds_quota: process.file_descriptors
84-
}.merge(metrics_data_for_instance(stats, quota_stats, log_cache_errors, Time.now.to_datetime.rfc3339, actual_lrp.actual_lrp_key.index))
119+
}.merge(metrics_data_for_instance(stats, quota_stats, log_cache_errors, Time.now.utc.to_datetime.rfc3339, actual_lrp.actual_lrp_key.index))
85120
}
86121
info[:details] = actual_lrp.placement_error if actual_lrp.placement_error.present?
87122

lib/diego/client.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,17 @@ def actual_lrps_by_process_guid(process_guid)
152152
protobuf_decode!(response.body, Bbs::Models::ActualLRPsResponse)
153153
end
154154

155+
def actual_lrps_by_process_guids(process_guids)
156+
request = protobuf_encode!({ process_guids: }, Bbs::Models::ActualLRPsByProcessGuidsRequest)
157+
158+
response = with_request_error_handling do
159+
client.post(Routes::ACTUAL_LRPS_BY_PROCESS_GUIDS, request, headers)
160+
end
161+
162+
validate_status_200!(response)
163+
protobuf_decode!(response.body, Bbs::Models::ActualLRPsByProcessGuidsResponse)
164+
end
165+
155166
def with_request_error_handling
156167
delay = 0.25
157168
max_delay = 5

0 commit comments

Comments
 (0)