Skip to content

Commit 7cf6d4b

Browse files
philippthunSven Krieger
andauthored
Implement GET /v3/processes/:guid/process_instances and embed=process_instances (#4796)
* 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> * Implement embed=process_instances Process instances can be embedded into process resources; the parameter embed=process_instances is available for the following API endpoints: - GET /v3/processes/:guid?embed=process_instances - GET /v3/apps/:guid/processes/:type?embed=process_instances - GET /v3/processes?embed=process_instances - GET /v3/apps/:guid/processes?embed=process_instances A new ProcessShowMessage has been introduced; ProcessesListMessage has been extended. Both messages allow the 'embed' key that is checked with the EmbedParamValidator. The process output is enhanced by the EmbedProcessInstancesDecorator that fetches the required information from Diego and adds a field 'process_instances' to the resource. Process resources now also contain a link to 'process_instances'. API docs have been extended with the new process instances pseudo-resource and a description of the 'embed' parameter. The 'embed' parameter is also described in the V3 style guide. --------- Co-authored-by: Sven Krieger <sven.krieger@sap.com>
1 parent e51d95c commit 7cf6d4b

34 files changed

+1157
-53
lines changed

.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: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
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'
5+
require 'decorators/embed_process_instances_decorator'
46
require 'cloud_controller/paging/pagination_options'
57
require 'actions/process_delete'
68
require 'fetchers/process_list_fetcher'
@@ -9,6 +11,7 @@
911
require 'actions/process_terminate'
1012
require 'actions/process_update'
1113
require 'messages/process_scale_message'
14+
require 'messages/process_show_message'
1215
require 'messages/process_update_message'
1316
require 'messages/processes_list_message'
1417
require 'controllers/v3/mixins/app_sub_resource'
@@ -40,17 +43,30 @@ def index
4043
end
4144
end
4245

46+
decorators = []
47+
decorators << EmbedProcessInstancesDecorator if EmbedProcessInstancesDecorator.match?(message.embed)
48+
4349
render status: :ok, json: Presenters::V3::PaginatedListPresenter.new(
4450
presenter: Presenters::V3::ProcessPresenter,
4551
paginated_result: SequelPaginator.new.get_page(dataset, message.try(:pagination_options)),
4652
path: base_url(resource: 'processes'),
47-
message: message
53+
message: message,
54+
decorators: decorators
4855
)
4956
end
5057

5158
def show
52-
# TODO
53-
render status: :ok, json: Presenters::V3::ProcessPresenter.new(@process, show_secrets: permission_queryer.can_read_secrets_in_space?(@space.id, @space.organization_id))
59+
message = ProcessShowMessage.from_params(query_params)
60+
invalid_param!(message.errors.full_messages) unless message.valid?
61+
62+
decorators = []
63+
decorators << EmbedProcessInstancesDecorator if EmbedProcessInstancesDecorator.match?(message.embed)
64+
65+
render status: :ok, json: Presenters::V3::ProcessPresenter.new(
66+
@process,
67+
show_secrets: permission_queryer.can_read_secrets_in_space?(@space.id, @space.organization_id),
68+
decorators: decorators
69+
)
5470
end
5571

5672
def update
@@ -106,6 +122,12 @@ def stats
106122
render status: :ok, json: Presenters::V3::ProcessStatsPresenter.new(@process.type, process_stats)
107123
end
108124

125+
def process_instances
126+
instances = instances_reporters.instances_for_processes([@process])
127+
128+
render status: :ok, json: Presenters::V3::ProcessInstancesPresenter.new(instances[@process.guid], @process)
129+
end
130+
109131
private
110132

111133
def find_process_and_space
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module VCAP::CloudController
2+
class EmbedProcessInstancesDecorator
3+
class << self
4+
def match?(embed)
5+
embed&.include?('process_instances')
6+
end
7+
8+
def decorate(hash, processes)
9+
instances_reporters = CloudController::DependencyLocator.instance.instances_reporters
10+
instances = instances_reporters.instances_for_processes(processes)
11+
12+
if hash.key?(:resources)
13+
# Decorate PaginatedListPresenter
14+
processes.each do |process|
15+
resource_index = hash[:resources].find_index { |resource| resource[:guid] == process.guid }
16+
next unless resource_index # Should not happen...
17+
18+
hash[:resources][resource_index] = embed_process_instances(hash[:resources][resource_index], process_instances(instances, process.guid))
19+
end
20+
else
21+
# Decorate ProcessPresenter
22+
hash = embed_process_instances(hash, process_instances(instances, hash[:guid]))
23+
end
24+
25+
hash
26+
end
27+
28+
private
29+
30+
def process_instances(instances, process_guid)
31+
instances[process_guid]&.map do |index, instance|
32+
{
33+
index: index,
34+
state: instance[:state],
35+
since: instance[:since]
36+
}
37+
end || []
38+
end
39+
40+
def embed_process_instances(resource_hash, process_instances)
41+
hash_as_array = resource_hash.to_a
42+
before_relationships = hash_as_array.index { |k, _| k == :relationships } || hash_as_array.length
43+
hash_as_array.insert(before_relationships, [:process_instances, process_instances])
44+
hash_as_array.to_h
45+
end
46+
end
47+
end
48+
end

app/messages/base_message.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,22 @@ def validate(record)
139139
end
140140
end
141141

142+
class EmbedParamValidator < ActiveModel::Validator
143+
def validate(record)
144+
return unless record.requested?(:embed)
145+
146+
key_counts = Hash.new(0)
147+
record.embed.each do |embed_candidate|
148+
if options[:valid_values].member?(embed_candidate)
149+
key_counts[embed_candidate] += 1
150+
record.errors.add(:base, message: "Duplicate embedded resource: '#{embed_candidate}'") if key_counts[embed_candidate] == 2
151+
else
152+
record.errors.add(:base, message: "Invalid embedded resource: '#{embed_candidate}'. Valid embedded resources are: '#{options[:valid_values].join("', '")}'")
153+
end
154+
end
155+
end
156+
end
157+
142158
class LifecycleTypeParamValidator < ActiveModel::Validator
143159
def validate(record)
144160
return unless record.requested?(:lifecycle_type)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require 'messages/base_message'
2+
3+
module VCAP::CloudController
4+
class ProcessShowMessage < BaseMessage
5+
register_allowed_keys [:embed]
6+
7+
validates_with NoAdditionalParamsValidator
8+
validates_with EmbedParamValidator, valid_values: ['process_instances']
9+
10+
def self.from_params(params)
11+
super(params, %w[embed])
12+
end
13+
end
14+
end

app/messages/processes_list_message.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,19 @@ class ProcessesListMessage < MetadataListMessage
88
space_guids
99
organization_guids
1010
app_guids
11+
embed
1112
]
1213

1314
validates_with NoAdditionalParamsValidator # from BaseMessage
15+
validates_with EmbedParamValidator, valid_values: ['process_instances']
1416

17+
validates :space_guids, array: true, allow_nil: true
18+
validates :organization_guids, array: true, allow_nil: true
1519
validates :app_guids, array: true, allow_nil: true
1620
validate :app_nested_request, if: -> { app_guid.present? }
1721

1822
def self.from_params(params)
19-
super(params, %w[types space_guids organization_guids app_guids])
23+
super(params, %w[types space_guids organization_guids app_guids embed])
2024
end
2125

2226
def to_param_hash

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: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,14 @@ 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
2114

2215
readiness_health_check_data = { invocation_timeout: process.readiness_health_check_invocation_timeout, interval: process.readiness_health_check_interval }
2316
readiness_health_check_data[:endpoint] = process.readiness_health_check_http_endpoint if process.readiness_health_check_type == HealthCheckTypes::HTTP
24-
{
17+
18+
hash = {
2519
guid: process.guid,
2620
created_at: process.created_at,
2721
updated_at: process.updated_at,
@@ -51,6 +45,8 @@ def to_hash
5145
},
5246
links: build_links
5347
}
48+
49+
@decorators.reduce(hash) { |memo, d| d.decorate(memo, [process]) }
5450
end
5551

5652
private
@@ -73,7 +69,8 @@ def build_links
7369
scale: { href: url_builder.build_url(path: "/v3/processes/#{process.guid}/actions/scale"), method: 'POST' },
7470
app: { href: url_builder.build_url(path: "/v3/apps/#{process.app_guid}") },
7571
space: { href: url_builder.build_url(path: "/v3/spaces/#{process.space_guid}") },
76-
stats: { href: url_builder.build_url(path: "/v3/processes/#{process.guid}/stats") }
72+
stats: { href: url_builder.build_url(path: "/v3/processes/#{process.guid}/stats") },
73+
process_instances: { href: url_builder.build_url(path: "/v3/processes/#{process.guid}/process_instances") }
7774
}
7875
end
7976
end

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'

0 commit comments

Comments
 (0)