Permalink
Browse files

Minor refactoring

Passing in the set of plugins that can be run is more logical,
cleaner, and makes testing easier.

Test plan:
- Unit tests pass

Change-Id: Id52d09f778417b42a56fd30a0a53967555d76f71
  • Loading branch information...
1 parent 466d8ad commit e0d21706577c2463250e23028495e4820e76f920 mpage committed Nov 12, 2011
View
@@ -4,20 +4,38 @@ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
require 'bundler/setup'
require 'yaml'
-if ARGV.length != 2
- $stderr.puts "Usage: plugin_runner [serialized plugin runner path] [error path]"
+require 'vcap/plugin_registry'
+require 'vcap/stager/plugin_runner'
+
+# task_path should be the location of a task file serialized with
+# VCAP::Stager::PluginRunner.serialize_task
+
+if ARGV.length != 1
+ $stderr.puts "Usage: plugin_runner [task_path]"
exit 1
end
-plugin_runner_path, error_path = ARGV
-
+task = VCAP::PluginRunner.deserialize_task(ARGV[0])
begin
- plugin_runner = VCAP::Stager::PluginRunner.from_file(plugin_runner_path)
- plugin_runner.run_plugins
+ if task['opts']['config_dir']
+ VCAP::PluginRegistry.configure_plugins(task['opts']['config_dir'])
+ end
+
+ if task['opts']['log_path']
+ VCAP::Logging.setup_from_config(:log_file => task['opts']['log_path'])
+ else
+ # Logs to stdout by default
+ VCAP::Logging.setup_from_config({})
+ end
+
+ plugin_runner = VCAP::Stager::PluginRunner.new(VCAP::PluginRegistry.plugins)
+ plugin_runner.run_plugins(task['source_dir'], task['dest_dir'], task['app_props'], task['cc_info'])
exit 0
rescue => e
- File.open(error_path, 'w+') do |f|
- YAML.dump(e, f)
+ if task['opts']['error_path']
+ File.open(task['opts']['error_path'], 'w+') do |f|
+ YAML.dump(e, f)
+ end
end
exit 1
-end
+end
@@ -22,100 +22,111 @@ class VCAP::Stager::PluginRunner
RUNNER_BIN_PATH = File.join(VCAP::Stager::BIN_DIR, 'plugin_runner')
class << self
- def from_file(src_path)
- YAML.load_file(src_path)
+ # Writes out necessary information for invoking the plugin runner using
+ # the standalone script.
+ #
+ # @param task_path String Where to serialize the task
+ # @param opts Hash 'error_path' => Where to store any exceptions
+ # thrown during the process.
+ # 'config_dir' => Optional. Directory containing
+ # config files for staging plugins.
+ # 'log_path' => Optional. Where to log plugin output.
+ # If not provided, logs will go to stdout.
+ def serialize_task(task_path, source_dir, dest_dir, app_props, cc_info, opts={})
+ task = {
+ 'source_dir' => source_dir,
+ 'dest_dir' => dest_dir,
+ 'app_props' => app_props,
+ 'cc_info' => cc_info,
+ 'opts' => opts,
+ }
+ File.open(task_path, 'w+') do |f|
+ YAML.dump(task, f)
+ end
end
- end
- # @param source_dir String Directory containing application source
- # @param dest_dir String Directory where the staged droplet should live
- # @param app_properties
- # @param cc_info Hash Information needed for contacting the CC
- # 'host'
- # 'port'
- # 'task_id'
- def initialize(source_dir, dest_dir, app_properties, cc_info)
- @source_dir = source_dir
- @dest_dir = dest_dir
- @droplet = VCAP::Stager::Droplet.new(dest_dir)
- @app_properties = app_properties
- @services_client = VCAP::CloudController::Ipc::ServiceConsumerV1Client.new(cc_info['host'],
- cc_info['port'],
- :staging_task_id => cc_info['task_id'])
- @environment = {}
+ def deserialize_task(task_path)
+ YAML.load_file(task_path)
+ end
end
- def run_plugins
- @logger = VCAP::Logging.logger('vcap.stager.plugin_runner')
-
- framework_plugin, feature_plugins = collect_plugins
-
- @logger.info("Setting up base droplet structure")
- @droplet.create_skeleton(@source_dir)
+ # @param plugins Array Available plugins
+ # name => plugin
+ def initialize(plugins={})
+ @plugins = plugins
+ end
- actions = VCAP::Stager::PluginActionProxy.new(@droplet.framework_start_path,
- @droplet.framework_stop_path,
- @droplet,
- @services_client,
- @environment)
- @logger.info("Running framework plugin: #{framework_plugin.name}")
- framework_plugin.stage(@droplet.app_source_dir, actions, @app_properties)
+ # Runs the appropriate plugins to stage the given application
+ #
+ # @param source_dir String Directory containing application source
+ # @param dest_dir String Directory where the staged droplet should live
+ # @param app_props Hash Application properties.
+ # @param cc_info Hash Information needed for contacting the CC
+ # 'host'
+ # 'port'
+ # 'task_id'
+ def run_plugins(source_dir, dest_dir, app_props, cc_info)
+ environment = {}
+ droplet = VCAP::Stager::Droplet.new(dest_dir)
+ services_client = VCAP::CloudController::Ipc::ServiceConsumerV1Client.new(cc_info['host'],
+ cc_info['port'],
+ :staging_task_id => cc_info['task_id'])
+ logger = VCAP::Logging.logger('vcap.stager.plugin_runner')
+
+ framework_plugin, feature_plugins = collect_plugins(app_props['framework'], app_props['plugins'].keys)
+
+ logger.info("Setting up base droplet structure")
+ droplet.create_skeleton(source_dir)
+
+ actions = VCAP::Stager::PluginActionProxy.new(droplet.framework_start_path,
+ droplet.framework_stop_path,
+ droplet,
+ services_client,
+ environment)
+ logger.info("Running framework plugin: #{framework_plugin.name}")
+ framework_plugin.stage(droplet.app_source_dir, actions, app_props)
for feature_plugin in feature_plugins
pname = feature_plugin.name
- actions = VCAP::Stager::PluginActionProxy.new(@droplet.feature_start_path(pname),
- @droplet.feature_stop_path(pname),
- @droplet,
- @services_client,
- @environment)
- @logger.info("Running feature plugin: #{feature_plugin.name}")
- feature_plugin.stage(framework_plugin, @droplet.app_source_dir, actions, @app_properties)
+ actions = VCAP::Stager::PluginActionProxy.new(droplet.feature_start_path(pname),
+ droplet.feature_stop_path(pname),
+ droplet,
+ services_client,
+ environment)
+ logger.info("Running feature plugin: #{feature_plugin.name}")
+ feature_plugin.stage(framework_plugin, droplet.app_source_dir, actions, app_props)
end
- @droplet.generate_vcap_start_script(@environment)
+ droplet.generate_vcap_start_script(environment)
end
- # Serializes *self* to the supplied path
+ # Returns the plugins that will be executed, in order.
#
- # @param dest_path String Where to serialize ourselves
- def to_file(dest_path)
- File.open(dest_path, 'w+') do |f|
- YAML.dump(self, f)
- end
- end
-
- protected
-
- def collect_plugins
- framework_plugin = nil
+ # @param framework String Framework of the application being staged
+ # @param plugins Array[String] List of plugin names
+ #
+ # @return Array [framework_plugin, *feature_plugins]
+ def collect_plugins(framework, plugins)
feature_plugins = []
+ framework_plugin = nil
- for name in @app_properties['plugins'].keys
- plugin = VCAP::PluginRegistry.plugins[name]
- unless plugin
+ for name in plugins
+ plugin = @plugins[name]
+ unless plugin || plugin.respond_to?(:stage)
raise VCAP::Stager::UnsupportedPluginError, name
end
- ptype = plugin.staging_plugin_type
- case ptype
- when :framework
- @logger.debug("Found framework plugin: #{name}")
- if framework_plugin
- errstr = [framework_plugin.name, name].join(', ')
- raise VCAP::Stager::DuplicateFrameworkPluginError, errstr
+ if plugin.respond_to?(:framework)
+ if plugin.framework != framework
+ raise VCAP::Stager::DuplicateFrameworkPluginError, plugin.name
else
framework_plugin = plugin
end
-
- when :feature
- @logger.debug("Found feature plugin: #{name}")
- feature_plugins << plugin
-
else
- raise VCAP::Stager::UnknownPluginTypeError, ptype
-
+ feature_plugins << plugin
end
end
+
unless framework_plugin
raise VCAP::Stager::MissingFrameworkPluginError
end
@@ -251,13 +251,14 @@ def run_staging_plugin(src_dir, dst_dir, work_dir, task_logger)
# @param dst_dir String Where to place the staged app
# @param base_dir String Directory to use to place scratch files (houses {src,dst} dir)
def run_plugins(src_dir, dst_dir, base_dir)
- runner_path = File.join(base_dir, 'plugin_runner')
- gemfile_path = File.join(base_dir, 'Gemfile')
- error_path = File.join(base_dir, 'plugin_runner_error')
+ task_path = File.join(base_dir, 'task')
+ task_opts = {
+ 'log_path' => File.join(base_dir, 'staging.log'),
+ 'error_path' => File.join(base_dir, 'plugin_runner_error'),
+ }
- runner = VCAP::Stager::PluginRunner.new(src_dir, dst_dir, @app_props, @cc_info)
- runner.to_file(runner_path)
- runner_cmd = "#{@ruby_path} %s %s" % [VCAP::Stager::PluginRunner::RUNNER_BIN_PATH, error_path]
+ VCAP::Stager::PluginRunner.serialize_task(task_path, src_dir, dst_dir, @app_props, @cc_info, task_opts)
+ runner_cmd = "#{@ruby_path} %s %s" % [VCAP::Stager::PluginRunner::RUNNER_BIN_PATH, task_path]
@vcap_logger.debug("Executing plugin runner with command #{runner_cmd}")
res = run_logged(runner_cmd, 0, @max_staging_duration)
@@ -270,9 +271,9 @@ def run_plugins(src_dir, dst_dir, base_dir)
@vcap_logger.error("Failed running staging plugins")
begin
- err = YAML.load_file(error_path)
+ err = YAML.load_file(task_opts['error_path'])
rescue => e
- @vcap_logger.error("Failed reading error file at #{error_path}")
+ @vcap_logger.error("Failed reading error file at #{task_opts['error_path']}")
@vcap_logger.error(e)
# We can't communicate the specific reason that staging failed (if any),
# so just raise a generic error.
@@ -27,7 +27,6 @@
'fds' => 1024,
}
}
- VCAP::PluginRegistry.plugins = {}
end
after :each do
@@ -37,61 +36,52 @@
it 'should raise an error for unknown plugins' do
@app_props['plugins']['unknown'] = {}
- orch = VCAP::Stager::PluginRunner.new(@src_dir, @dst_dir, @app_props, @cc_info)
+ runner = VCAP::Stager::PluginRunner.new
expect do
- orch.run_plugins
+ runner.run_plugins(@src_dir, @dst_dir, @app_props, @cc_info)
end.to raise_error(VCAP::Stager::UnsupportedPluginError)
end
it 'should raise an error if no framework plugin is supplied' do
- orch = VCAP::Stager::PluginRunner.new(@src_dir, @dst_dir, @app_props, @cc_info)
+ runner = VCAP::Stager::PluginRunner.new
expect do
- orch.run_plugins
+ runner.run_plugins(@src_dir, @dst_dir, @app_props, @cc_info)
end.to raise_error(VCAP::Stager::MissingFrameworkPluginError)
end
it 'should raise an error if > 1 framework plugins are supplied' do
- plugins = []
+ frameworks = ['sinatra', 'unknown']
+ plugins = {}
2.times do |i|
name = "plugin_#{i}"
@app_props['plugins'][name] = {}
- p = create_mock_plugin(name, :framework)
- VCAP::PluginRegistry.register_plugins(p)
+ plugins[name] = create_mock_plugin(name, frameworks[i])
end
- orch = VCAP::Stager::PluginRunner.new(@src_dir, @dst_dir, @app_props, @cc_info)
+ runner = VCAP::Stager::PluginRunner.new(plugins)
expect do
- orch.run_plugins
+ runner.run_plugins(@src_dir, @dst_dir, @app_props, @cc_info)
end.to raise_error(VCAP::Stager::DuplicateFrameworkPluginError)
end
- it 'should raise an error if a plugin of unknown type is supplied' do
- p = create_mock_plugin('plugin0', :invalid_plugin_type)
- VCAP::PluginRegistry.register_plugins(p)
- @app_props['plugins']['plugin0'] = {}
- orch = VCAP::Stager::PluginRunner.new(@src_dir, @dst_dir, @app_props, @cc_info)
- expect do
- orch.run_plugins
- end.to raise_error(VCAP::Stager::UnknownPluginTypeError)
- end
-
it 'should call stage on each of the registered plugins' do
- plugin_types = [:framework, :feature, :feature]
- plugin_types.each_with_index do |ptype, ii|
+ plugins = {}
+ frameworks = ['sinatra', nil, nil]
+ frameworks.each_with_index do |framework, ii|
name = "plugin_#{ii}"
@app_props['plugins'][name] = {}
- p = create_mock_plugin(name, ptype)
+ p = create_mock_plugin(name, framework)
p.should_receive(:stage).with(any_args())
- VCAP::PluginRegistry.register_plugins(p)
+ plugins[name] = p
end
- orch = VCAP::Stager::PluginRunner.new(@src_dir, @dst_dir, @app_props, @cc_info)
- orch.run_plugins
+ runner = VCAP::Stager::PluginRunner.new(plugins)
+ runner.run_plugins(@src_dir, @dst_dir, @app_props, @cc_info)
end
end
- def create_mock_plugin(name, type)
- ret = mock(name)
- ret.stub(:staging_plugin_type).and_return(type)
+ def create_mock_plugin(name, framework=nil)
+ ret = mock()
ret.stub(:name).and_return(name)
+ ret.stub(:framework).and_return(framework) if framework
ret
end
end

0 comments on commit e0d2170

Please sign in to comment.