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
2 changes: 0 additions & 2 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ Lint/UnusedMethodArgument:
Lint/UselessMethodDefinition:
Exclude:
- 'app/messages/route_destination_update_message.rb'
- 'app/presenters/v3/buildpack_presenter.rb'
- 'app/presenters/v3/process_presenter.rb'
- 'spec/support/fake_front_controller.rb'

# Offense count: 791
Expand Down
28 changes: 25 additions & 3 deletions app/controllers/v3/processes_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
require 'presenters/v3/paginated_list_presenter'
require 'presenters/v3/process_presenter'
require 'presenters/v3/process_stats_presenter'
require 'presenters/v3/process_instances_presenter'
require 'decorators/embed_process_instances_decorator'
require 'cloud_controller/paging/pagination_options'
require 'actions/process_delete'
require 'fetchers/process_list_fetcher'
Expand All @@ -9,6 +11,7 @@
require 'actions/process_terminate'
require 'actions/process_update'
require 'messages/process_scale_message'
require 'messages/process_show_message'
require 'messages/process_update_message'
require 'messages/processes_list_message'
require 'controllers/v3/mixins/app_sub_resource'
Expand Down Expand Up @@ -40,17 +43,30 @@ def index
end
end

decorators = []
decorators << EmbedProcessInstancesDecorator if EmbedProcessInstancesDecorator.match?(message.embed)

render status: :ok, json: Presenters::V3::PaginatedListPresenter.new(
presenter: Presenters::V3::ProcessPresenter,
paginated_result: SequelPaginator.new.get_page(dataset, message.try(:pagination_options)),
path: base_url(resource: 'processes'),
message: message
message: message,
decorators: decorators
)
end

def show
# TODO
render status: :ok, json: Presenters::V3::ProcessPresenter.new(@process, show_secrets: permission_queryer.can_read_secrets_in_space?(@space.id, @space.organization_id))
message = ProcessShowMessage.from_params(query_params)
invalid_param!(message.errors.full_messages) unless message.valid?

decorators = []
decorators << EmbedProcessInstancesDecorator if EmbedProcessInstancesDecorator.match?(message.embed)

render status: :ok, json: Presenters::V3::ProcessPresenter.new(
@process,
show_secrets: permission_queryer.can_read_secrets_in_space?(@space.id, @space.organization_id),
decorators: decorators
)
end

def update
Expand Down Expand Up @@ -106,6 +122,12 @@ def stats
render status: :ok, json: Presenters::V3::ProcessStatsPresenter.new(@process.type, process_stats)
end

def process_instances
instances = instances_reporters.instances_for_processes([@process])

render status: :ok, json: Presenters::V3::ProcessInstancesPresenter.new(instances[@process.guid], @process)
end

private

def find_process_and_space
Expand Down
48 changes: 48 additions & 0 deletions app/decorators/embed_process_instances_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module VCAP::CloudController
class EmbedProcessInstancesDecorator
class << self
def match?(embed)
embed&.include?('process_instances')
end

def decorate(hash, processes)
instances_reporters = CloudController::DependencyLocator.instance.instances_reporters
instances = instances_reporters.instances_for_processes(processes)

if hash.key?(:resources)
# Decorate PaginatedListPresenter
processes.each do |process|
resource_index = hash[:resources].find_index { |resource| resource[:guid] == process.guid }
next unless resource_index # Should not happen...

hash[:resources][resource_index] = embed_process_instances(hash[:resources][resource_index], process_instances(instances, process.guid))
end
else
# Decorate ProcessPresenter
hash = embed_process_instances(hash, process_instances(instances, hash[:guid]))
end

hash
end

private

def process_instances(instances, process_guid)
instances[process_guid].map do |index, instance|
{
index: index,
state: instance[:state],
since: instance[:since]
}
end
end

def embed_process_instances(resource_hash, process_instances)
hash_as_array = resource_hash.to_a
before_relationships = hash_as_array.index { |k, _| k == :relationships } || hash_as_array.length
hash_as_array.insert(before_relationships, [:process_instances, process_instances])
hash_as_array.to_h
end
end
end
end
16 changes: 16 additions & 0 deletions app/messages/base_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,22 @@ def validate(record)
end
end

class EmbedParamValidator < ActiveModel::Validator
def validate(record)
return unless record.requested?(:embed)

key_counts = Hash.new(0)
record.embed.each do |embed_candidate|
if options[:valid_values].member?(embed_candidate)
key_counts[embed_candidate] += 1
record.errors.add(:base, message: "Duplicate embedded resource: '#{embed_candidate}'") if key_counts[embed_candidate] == 2
else
record.errors.add(:base, message: "Invalid embedded resource: '#{embed_candidate}'. Valid embedded resources are: '#{options[:valid_values].join("', '")}'")
end
end
end
end

class LifecycleTypeParamValidator < ActiveModel::Validator
def validate(record)
return unless record.requested?(:lifecycle_type)
Expand Down
14 changes: 14 additions & 0 deletions app/messages/process_show_message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'messages/base_message'

module VCAP::CloudController
class ProcessShowMessage < BaseMessage
register_allowed_keys [:embed]

validates_with NoAdditionalParamsValidator
validates_with EmbedParamValidator, valid_values: ['process_instances']

def self.from_params(params)
super(params, %w[embed])
end
end
end
6 changes: 5 additions & 1 deletion app/messages/processes_list_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ class ProcessesListMessage < MetadataListMessage
space_guids
organization_guids
app_guids
embed
]

validates_with NoAdditionalParamsValidator # from BaseMessage
validates_with EmbedParamValidator, valid_values: ['process_instances']

# validates :space_guids, array: true, allow_nil: true
# validates :organization_guids, array: true, allow_nil: true
validates :app_guids, array: true, allow_nil: true
validate :app_nested_request, if: -> { app_guid.present? }

def self.from_params(params)
super(params, %w[types space_guids organization_guids app_guids])
super(params, %w[types space_guids organization_guids app_guids embed])
end

def to_param_hash
Expand Down
7 changes: 0 additions & 7 deletions app/presenters/v3/buildpack_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@ def to_hash
}
end

class << self
# :labels and :annotations come from MetadataPresentationHelpers
def associated_resources
super
end
end

private

def buildpack
Expand Down
47 changes: 47 additions & 0 deletions app/presenters/v3/process_instances_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require 'presenters/v3/base_presenter'
require 'presenters/mixins/metadata_presentation_helpers'

module VCAP::CloudController
module Presenters
module V3
class ProcessInstancesPresenter < BasePresenter
attr_reader :process

def initialize(instances, process)
super(instances)
@process = process
end

def to_hash
{
resources: build_instances,
links: build_links
}
end

private

def instances
@resource
end

def build_instances
instances.map do |index, instance|
{
index: index,
state: instance[:state],
since: instance[:since]
}
end
end

def build_links
{
self: { href: url_builder.build_url(path: "/v3/processes/#{process.guid}/process_instances") },
process: { href: url_builder.build_url(path: "/v3/processes/#{process.guid}") }
}
end
end
end
end
end
12 changes: 4 additions & 8 deletions app/presenters/v3/process_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,14 @@ module V3
class ProcessPresenter < BasePresenter
include VCAP::CloudController::Presenters::Mixins::MetadataPresentationHelpers

class << self
# :labels and :annotations come from MetadataPresentationHelpers
def associated_resources
super
end
end

def to_hash
health_check_data = { timeout: process.health_check_timeout, invocation_timeout: process.health_check_invocation_timeout, interval: process.health_check_interval }
health_check_data[:endpoint] = process.health_check_http_endpoint if process.health_check_type == HealthCheckTypes::HTTP

readiness_health_check_data = { invocation_timeout: process.readiness_health_check_invocation_timeout, interval: process.readiness_health_check_interval }
readiness_health_check_data[:endpoint] = process.readiness_health_check_http_endpoint if process.readiness_health_check_type == HealthCheckTypes::HTTP
{

hash = {
guid: process.guid,
created_at: process.created_at,
updated_at: process.updated_at,
Expand Down Expand Up @@ -51,6 +45,8 @@ def to_hash
},
links: build_links
}

@decorators.reduce(hash) { |memo, d| d.decorate(memo, [process]) }
end

private
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
get '/processes', to: 'processes#index'
get '/processes/:process_guid', to: 'processes#show'
patch '/processes/:process_guid', to: 'processes#update'
get '/processes/:process_guid/process_instances', to: 'processes#process_instances'
delete '/processes/:process_guid/instances/:index', to: 'processes#terminate'
post '/processes/:process_guid/actions/scale', to: 'processes#scale'
get '/processes/:process_guid/stats', to: 'processes#stats'
Expand Down
3 changes: 2 additions & 1 deletion lib/cloud_controller/backends/instances_reporters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def stats_for_app(app)
end

delegate :number_of_starting_and_running_instances_for_processes, :instance_count_summary, to: :diego_reporter
delegate :instances_for_processes, to: :diego_stats_reporter

private

Expand All @@ -36,7 +37,7 @@ def diego_reporter
end

def diego_stats_reporter
Diego::InstancesStatsReporter.new(dependency_locator.bbs_instances_client, dependency_locator.log_cache_metrics_client)
@diego_stats_reporter ||= Diego::InstancesStatsReporter.new(dependency_locator.bbs_instances_client, dependency_locator.log_cache_metrics_client)
end

def dependency_locator
Expand Down
22 changes: 17 additions & 5 deletions lib/cloud_controller/diego/bbs_instances_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def lrp_instances(process)
process_guid = ProcessGuid.from_process(process)
logger.info('lrp.instances.request', process_guid:)

actual_lrps_response = handle_diego_errors(process_guid) do
actual_lrps_response = handle_diego_errors do
response = @client.actual_lrps_by_process_guid(process_guid)
logger.info('lrp.instances.response', process_guid: process_guid, error: response.error)
response
Expand All @@ -20,30 +20,42 @@ def lrp_instances(process)
actual_lrps_response.actual_lrps
end

def actual_lrps_by_processes(processes)
process_guids = processes.map { |process| ProcessGuid.from_process(process) }
logger.info('actual.lrps.by.processes.request', process_guids:)

actual_lrps_response = handle_diego_errors do
response = @client.actual_lrps_by_process_guids(process_guids)
logger.info('actual.lrps.by.processes.response', process_guids: process_guids, error: response.error)
response
end

actual_lrps_response.actual_lrps
end

def desired_lrp_instance(process)
process_guid = ProcessGuid.from_process(process)
response = handle_diego_errors(process_guid) do
response = handle_diego_errors(handle_resource_not_found: true, process_guid: process_guid) do
@client.desired_lrp_by_process_guid(process_guid)
end
response.desired_lrp
end

private

def handle_diego_errors(process_guid)
def handle_diego_errors(handle_resource_not_found: false, process_guid: nil)
begin
response = yield
rescue ::Diego::Error => e
raise CloudController::Errors::InstancesUnavailable.new(e)
end

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

raise CloudController::Errors::InstancesUnavailable.new(response.error.message)

end

response
Expand Down
Loading