Skip to content

Commit

Permalink
First foray into switching V3 to ActionController
Browse files Browse the repository at this point in the history
- Switched ProcessController
- Tests work (mostly)
- Lots to do still

[#98146936]

Signed-off-by: Zach Robinson <zrobinson@pivotal.io>
  • Loading branch information
zrob authored and pivotal committed Nov 11, 2015
1 parent a9192f1 commit 798e0dd
Show file tree
Hide file tree
Showing 16 changed files with 725 additions and 383 deletions.
3 changes: 2 additions & 1 deletion Gemfile
Expand Up @@ -2,7 +2,7 @@
source 'http://rubygems.org'

gem 'addressable'
gem 'activesupport'
gem 'railties'
gem 'rake'
gem 'eventmachine', '~> 1.0.0'
gem 'fog'
Expand Down Expand Up @@ -57,6 +57,7 @@ group :test do
gem 'rspec_api_documentation', git: 'https://github.com/zipmark/rspec_api_documentation.git'
gem 'rspec-collection_matchers'
gem 'rspec-its'
gem 'rspec-rails'
gem 'rubocop'
gem 'astrolabe'
gem 'timecop'
Expand Down
12 changes: 0 additions & 12 deletions app/controllers/base/base_controller.rb
Expand Up @@ -202,18 +202,6 @@ def bad_request!(message)
raise VCAP::Errors::ApiError.new_from_details('MessageParseError', message)
end

def invalid_param!(message)
raise VCAP::Errors::ApiError.new_from_details('BadQueryParameter', message)
end

def unprocessable!(message)
raise VCAP::Errors::ApiError.new_from_details('UnprocessableEntity', message)
end

def unauthorized!
raise VCAP::Errors::ApiError.new_from_details('NotAuthorized')
end

attr_reader :config, :logger, :env, :params, :body, :request_attrs

class << self
Expand Down
77 changes: 77 additions & 0 deletions app/controllers/base/base_controller_v3.rb
@@ -0,0 +1,77 @@
require 'rails'
require 'action_controller'

module VCAP
module CloudController
module RestController
class ApplicationCC < ::Rails::Application
config.middleware.delete 'ActionDispatch::Session::CookieStore'
config.middleware.delete 'ActionDispatch::Cookies'
config.middleware.delete 'ActionDispatch::Flash'
end

module V3ErrorsHelper
def invalid_param!(message)
raise VCAP::Errors::ApiError.new_from_details('BadQueryParameter', message)
end

def unprocessable!(message)
raise VCAP::Errors::ApiError.new_from_details('UnprocessableEntity', message)
end

def unauthorized!
raise VCAP::Errors::ApiError.new_from_details('NotAuthorized')
end
end

class BaseControllerV3 < ::ActionController::Base
include VCAP::CloudController
include VCAP::CloudController::RestController::V3ErrorsHelper

wrap_parameters :body, format: [:json]

before_filter :set_current_user
before_filter :check_read_permissions!, only: [:index, :show]
before_filter :check_write_permissions!, except: [:index, :show]

def query_params
request.query_parameters.with_indifferent_access
end

# include VCAP::Errors
# include VCAP::RestAPI
# include Messages
# include Routes
# extend Forwardable

def check_read_permissions!
read_scope = SecurityContext.scopes.include?('cloud_controller.read')
raise VCAP::Errors::ApiError.new_from_details('NotAuthorized') if !roles.admin? && !read_scope
end

def check_write_permissions!
write_scope = SecurityContext.scopes.include?('cloud_controller.write')
raise VCAP::Errors::ApiError.new_from_details('NotAuthorized') if !roles.admin? && !write_scope
end

def set_current_user
auth_token = request.headers['HTTP_AUTHORIZATION']
token_decoder = VCAP::UaaTokenDecoder.new(Config.config[:uaa])
VCAP::CloudController::Security::SecurityContextConfigurer.new(token_decoder).configure(auth_token)
end

def roles
VCAP::CloudController::SecurityContext.roles
end

def current_user
VCAP::CloudController::SecurityContext.current_user
end

def current_user_email
VCAP::CloudController::SecurityContext.current_user_email
end
end
end
end
end
184 changes: 80 additions & 104 deletions app/controllers/v3/processes_controller.rb
Expand Up @@ -7,135 +7,111 @@
require 'actions/process_update'
require 'messages/process_update_message'
require 'messages/processes_list_message'
require 'controllers/base/base_controller_v3'

module VCAP::CloudController
class ProcessesController < RestController::BaseController
def self.dependencies
[:process_presenter, :index_stopper]
end

get '/v3/processes', :list
def list
check_read_permissions!

message = ProcessesListMessage.from_params(params)
invalid_param!(message.errors.full_messages) unless message.valid?

pagination_options = PaginationOptions.from_params(params)
invalid_param!(pagination_options.errors.full_messages) unless pagination_options.valid?
invalid_param!("Unknown query param(s) '#{params.keys.join("', '")}'") if params.any?

if roles.admin?
paginated_result = ProcessListFetcher.new.fetch_all(pagination_options)
else
space_guids = membership.space_guids_for_roles([Membership::SPACE_DEVELOPER, Membership::SPACE_MANAGER, Membership::SPACE_AUDITOR, Membership::ORG_MANAGER])
paginated_result = ProcessListFetcher.new.fetch(pagination_options, space_guids)
end

[HTTP::OK, @process_presenter.present_json_list(paginated_result, '/v3/processes')]
end

get '/v3/processes/:guid', :show
def show(guid)
check_read_permissions!
class ProcessesController < VCAP::CloudController::RestController::BaseControllerV3
def index
message = ProcessesListMessage.from_params(query_params)
invalid_param!(message.errors.full_messages) unless message.valid?

process = ProcessModel.where(guid: guid).eager(:space, :organization).all.first
pagination_options = PaginationOptions.from_params(query_params)
invalid_param!(pagination_options.errors.full_messages) unless pagination_options.valid?

not_found! if process.nil? || !can_read?(process.space.guid, process.organization.guid)

[HTTP::OK, @process_presenter.present_json(process)]
if roles.admin?
paginated_result = ProcessListFetcher.new.fetch_all(pagination_options)
else
space_guids = membership.space_guids_for_roles([Membership::SPACE_DEVELOPER, Membership::SPACE_MANAGER, Membership::SPACE_AUDITOR, Membership::ORG_MANAGER])
paginated_result = ProcessListFetcher.new.fetch(pagination_options, space_guids)
end

patch '/v3/processes/:guid', :update
def update(guid)
check_write_permissions!

request = parse_and_validate_json(body)
message = ProcessUpdateMessage.create_from_http_request(request)
unprocessable!(message.errors.full_messages) unless message.valid?

process = ProcessModel.where(guid: guid).eager(:space, :organization).all.first
not_found! if process.nil? || !can_read?(process.space.guid, process.organization.guid)
unauthorized! if !can_update?(process.space.guid)

ProcessUpdate.new(current_user, current_user_email).update(process, message)
render status: :ok, json: process_presenter.present_json_list(paginated_result, '/v3/processes')
end

[HTTP::OK, @process_presenter.present_json(process)]
rescue ProcessUpdate::InvalidProcess => e
unprocessable!(e.message)
end
def show
guid = params[:guid]
process = ProcessModel.where(guid: guid).eager(:space, :organization).all.first
not_found! if process.nil? || !can_read?(process.space.guid, process.organization.guid)
render status: :ok, json: process_presenter.present_json(process)
end

delete '/v3/processes/:guid/instances/:index', :terminate
def terminate(process_guid, process_index)
check_write_permissions!
def update
message = ProcessUpdateMessage.create_from_http_request(params[:body])
unprocessable!(message.errors.full_messages) unless message.valid?

process = ProcessModel.where(guid: process_guid).eager(:space, :organization).all.first
not_found! if process.nil? || !can_read?(process.space.guid, process.organization.guid)
unauthorized! unless can_terminate?(process.space.guid)
process = ProcessModel.where(guid: guid).eager(:space, :organization).all.first
not_found! if process.nil? || !can_read?(process.space.guid, process.organization.guid)
unauthorized! if !can_update?(process.space.guid)

instance_not_found! unless process_index.to_i < process.instances && process_index.to_i >= 0
ProcessUpdate.new(current_user, current_user_email).update(process, message)

index_stopper.stop_index(process, process_index.to_i)
render status: :ok, json: process_presenter.present_json(process)
rescue ProcessUpdate::InvalidProcess => e
unprocessable!(e.message)
end

[HTTP::NO_CONTENT, nil]
end
def terminate
process = ProcessModel.where(guid: process_guid).eager(:space, :organization).all.first
not_found! if process.nil? || !can_read?(process.space.guid, process.organization.guid)
unauthorized! unless can_terminate?(process.space.guid)

put '/v3/processes/:guid/scale', :scale
def scale(guid)
check_write_permissions!
index = params[:index].to_i
instance_not_found! unless index < process.instances && index >= 0

FeatureFlag.raise_unless_enabled!('app_scaling') unless roles.admin?
index_stopper.stop_index(process, index)

head :no_content
end

request = parse_and_validate_json(body)
message = ProcessScaleMessage.create_from_http_request(request)
unprocessable!(message.errors.full_messages) if message.invalid?
def scale
FeatureFlag.raise_unless_enabled!('app_scaling') unless roles.admin?

process, space, org = ProcessScaleFetcher.new.fetch(guid)
not_found! if process.nil? || !can_read?(space.guid, org.guid)
unauthorized! if !can_scale?(space.guid)
message = ProcessScaleMessage.create_from_http_request(params[:body])
unprocessable!(message.errors.full_messages) if message.invalid?

ProcessScale.new(current_user, current_user_email).scale(process, message)
process, space, org = ProcessScaleFetcher.new.fetch(params[:guid])
not_found! if process.nil? || !can_read?(space.guid, org.guid)
unauthorized! if !can_scale?(space.guid)

[HTTP::OK, @process_presenter.present_json(process)]
rescue ProcessScale::InvalidProcess => e
unprocessable!(e.message)
end
ProcessScale.new(current_user, current_user_email).scale(process, message)

protected
render status: :ok, json: process_presenter.present_json(process)
rescue ProcessScale::InvalidProcess => e
unprocessable!(e.message)
end

attr_reader :index_stopper
private

def inject_dependencies(dependencies)
@process_presenter = dependencies[:process_presenter]
@index_stopper = dependencies.fetch(:index_stopper)
end
def process_presenter
ProcessPresenter.new
end

private
def index_stopper
CloudController::DependencyLocator.instance.index_stopper
end

def membership
@membership ||= Membership.new(current_user)
end
def membership
@membership ||= Membership.new(current_user)
end

def can_read?(space_guid, org_guid)
roles.admin? ||
membership.has_any_roles?([Membership::SPACE_DEVELOPER,
Membership::SPACE_MANAGER,
Membership::SPACE_AUDITOR,
Membership::ORG_MANAGER], space_guid, org_guid)
end
def can_read?(space_guid, org_guid)
roles.admin? ||
membership.has_any_roles?([Membership::SPACE_DEVELOPER,
Membership::SPACE_MANAGER,
Membership::SPACE_AUDITOR,
Membership::ORG_MANAGER], space_guid, org_guid)
end

def can_update?(space_guid)
roles.admin? || membership.has_any_roles?([Membership::SPACE_DEVELOPER], space_guid)
end
alias_method :can_terminate?, :can_update?
alias_method :can_scale?, :can_update?
def can_update?(space_guid)
roles.admin? || membership.has_any_roles?([Membership::SPACE_DEVELOPER], space_guid)
end
alias_method :can_terminate?, :can_update?
alias_method :can_scale?, :can_update?

def instance_not_found!
raise VCAP::Errors::ApiError.new_from_details('ResourceNotFound', 'Instance not found')
end
def instance_not_found!
raise VCAP::Errors::ApiError.new_from_details('ResourceNotFound', 'Instance not found')
end

def not_found!
raise VCAP::Errors::ApiError.new_from_details('ResourceNotFound', 'Process not found')
end
def not_found!
raise VCAP::Errors::ApiError.new_from_details('ResourceNotFound', 'Process not found')
end
end
35 changes: 35 additions & 0 deletions config/environments/development.rb
@@ -0,0 +1,35 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.

# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false

# Do not eager load code on boot.
config.eager_load = false

# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false

# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log

# Debug mode disables concatenation and preprocessing of assets.
# This option may cause significant delays in view rendering with a large
# number of complex assets.
config.assets.debug = true

# Asset digests allow you to set far-future HTTP expiration dates on all assets,
# yet still be able to expire them through the digest params.
config.assets.digest = true

# Adds additional error checking when serving assets at runtime.
# Checks for improperly declared sprockets dependencies.
# Raises helpful error messages.
config.assets.raise_runtime_errors = true

# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
end

0 comments on commit 798e0dd

Please sign in to comment.