Skip to content
This repository has been archived by the owner on Jan 26, 2022. It is now read-only.

Commit

Permalink
Merge "Scaffolding for staging plugins" into staging_plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
mpage authored and testazuretrain committed Oct 27, 2011
2 parents 32688eb + 9329bec commit 22ef9dc
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 1 deletion.
7 changes: 6 additions & 1 deletion stager/lib/vcap/stager.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
require 'vcap/stager/app_properties'
require 'vcap/stager/constants'
require 'vcap/stager/config'
require 'vcap/stager/server'
require 'vcap/stager/plugin_action_proxy'
require 'vcap/stager/plugin_orchestrator'
require 'vcap/stager/plugin_orchestrator_error'
require 'vcap/stager/plugin_registry'
require 'vcap/stager/task'
require 'vcap/stager/task_error'
require 'vcap/stager/task_logger'
require 'vcap/stager/task_manager'
require 'vcap/stager/task_result'
require 'vcap/stager/server'
require 'vcap/stager/util'
require 'vcap/stager/version'

Expand Down
71 changes: 71 additions & 0 deletions stager/lib/vcap/stager/app_properties.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
require 'yajl'

require 'vcap/json_schema'

module VCAP
module Stager
end
end

class VCAP::Stager::AppProperties
SCHEMA = VCAP::JsonSchema.build do
{ :name => String,
:framework => String,
:runtime => String,
:plugins => Hash,
:environment => Hash,
:resource_limits => {
:memory => Integer,
:disk => Integer,
:fds => Integer,
},
:service_bindings => Array,
}
end

class << self
def decode(enc)
dec = Yajl::Parser.parse(enc, :symbolize_keys => true)
SCHEMA.validate(dec)
VCAP::Stager::AppProperties.new(dec[:name],
dec[:framework],
dec[:runtime],
dec[:plugins],
dec[:environment],
dec[:resource_limits],
dec[:service_bindings])
end
end

attr_accessor :name
attr_accessor :framework
attr_accessor :runtime
attr_accessor :plugins
attr_accessor :environment
attr_accessor :resource_limits
attr_accessor :service_bindings

def initialize(name, framework, runtime, plugins, environment, resource_limits, service_bindings)
@name = name
@framework = framework
@runtime = runtime
@plugins = plugins
@environment = environment
@resource_limits = resource_limits
@service_bindings = service_bindings
end

def encode
h = {
:name => @name,
:framework => @framework,
:runtime => @runtime,
:plugins => @plugins,
:environment => @environment,
:resource_limits => @resource_limits,
:service_bindings => @service_bindings,
}
Yajl::Encoder.encode(h)
end

end
42 changes: 42 additions & 0 deletions stager/lib/vcap/stager/plugin_action_proxy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module VCAP
module Stager
end
end

# This class exposes actions that modify VCAP resources to staging plugins. An instance
# of it is passed to the plugin as a parameter to the stage() method.
class VCAP::Stager::PluginActionProxy
# Creates a service on behalf of the user
#
# @param label String The label that uniquely identifies this service
# @param name String What to call the provisioned service
# @param plan String Which plan should be provisioned
# @param plan_option String Optional plan option to select.
def create_service(label, name, plan, plan_option=nil)
raise NotImplementedError
end

# Binds a service to the application being staged
#
# @param name String Name of service to bind
# @param binding_options Hash Service specific binding options
def bind_service(name, binding_options)
raise NotImplementedError
end

# Returns an open file object that the user can write contents of a
# start script into.
#
# @return File
def start_script
raise NotImplementedError
end

# Returns an open file object that the user can write contents of a stop
# script into.
#
# @return File
def stop_script
raise NotImplementedError
end
end
63 changes: 63 additions & 0 deletions stager/lib/vcap/stager/plugin_orchestrator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require 'rubygems'

require 'vcap/logging'

require 'vcap/stager/plugin_action_proxy'
require 'vcap/stager/plugin_orchestrator_error'
require 'vcap/stager/plugin_registry'

module VCAP
module Stager
end
end

# Responsible for orchestrating the execution of all staging plugins selected
# by the user.
class VCAP::Stager::PluginOrchestrator
# @param source_dir String Directory containing application source
# @param app_properties
def initialize(source_dir, app_properties)
@source_dir = source_dir
@app_properties = app_properties
@logger = VCAP::Logging.logger('vcap.stager.plugin_orchestrator')
end

def run_plugins
for name, props in @app_properties.plugins
require(name)
end

framework_plugin = nil
feature_plugins = []
for plugin in VCAP::Stager::PluginRegistry.plugins
ptype = plugin.plugin_type
case ptype
when :framework
@logger.debug("Found framework plugin: #{name}")
if framework_plugin
raise VCAP::Stager::DuplicateFrameworkPluginError, "Only one framework plugin allowed"
else
framework_plugin = plugin
end

when :feature
@logger.debug("Found feature plugin: #{name}")
feature_plugins << plugin

else
raise VCAP::Stager::UnknownPluginTypeError, "Unknown plugin type: #{ptype}"

end
end

raise VCAP::Stager::MissingFrameworkPluginError, "No framework plugin found" unless framework_plugin

actions = VCAP::Stager::PluginActionProxy.new
@logger.info("Running framework plugin: #{framework_plugin.name}")
framework_plugin.stage(@source_dir, actions, @app_properties)
for feature_plugin in feature_plugins
@logger.info("Running feature plugin: #{feature_plugin.name}")
feature_plugin.stage(framework_plugin, @source_dir, actions, @app_properties)
end
end
end
8 changes: 8 additions & 0 deletions stager/lib/vcap/stager/plugin_orchestrator_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module VCAP
module Stager
class PluginOrchestratorError < StandardError; end
class MissingFrameworkPluginError < PluginOrchestratorError; end
class DuplicateFrameworkPluginError < PluginOrchestratorError; end
class UnknownPluginTypeError < PluginOrchestratorError; end
end
end
35 changes: 35 additions & 0 deletions stager/lib/vcap/stager/plugin_registry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module VCAP
module Stager
end
end

# This is a simple container class for all plugins that should be invoked during
# staging. The contract between the stager and plugins is simple:
#
# A staging plugin MUST call VCAP::Stager::PluginRegistry.register_plugin() with
# an instance of itself when it is required.
class VCAP::Stager::PluginRegistry
class << self
def method_missing(method, *args, &block)
@registry ||= VCAP::Stager::PluginRegistry.new
@registry.send(method, *args, &block)
end
end

def initialize
@plugins = []
end

attr_reader :plugins

# Registers a plugin to be invoked during staging.
#
# @param Object Instance of a class implementing the staging plugin interface
def register_plugin(plugin)
@plugins << plugin
end

def reset
@plugins = []
end
end
80 changes: 80 additions & 0 deletions stager/spec/unit/plugin_orchestrator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
require File.join(File.dirname(__FILE__), 'spec_helper')

require 'fileutils'
require 'tmpdir'

describe VCAP::Stager::PluginOrchestrator do
describe '#run_plugins' do
before :each do
@tmpdir = Dir.mktmpdir
@app_props = VCAP::Stager::AppProperties.new('test',
'sinatra',
'ruby18',
{},
{},
{ :memory => 128,
:disk => 2048,
:fds => 1024},
:service_bindings => [])
VCAP::Stager::PluginRegistry.reset()
end

after :each do
FileUtils.rm_rf(@tmpdir)
end

it 'should raise an error for unknown plugins' do
@app_props.plugins = {'unknown_plugin' => {}}
orch = VCAP::Stager::PluginOrchestrator.new(@tmpdir, @app_props)
expect do
orch.run_plugins
end.to raise_error(LoadError)
end

it 'should raise an error if no framework plugin is supplied' do
orch = VCAP::Stager::PluginOrchestrator.new(@tmpdir, @app_props)
expect do
orch.run_plugins
end.to raise_error(VCAP::Stager::MissingFrameworkPluginError)
end

it 'should raise an error if > 1 framework plugins are supplied' do
plugins = []
2.times do |i|
p = create_mock_plugin("plugin_#{i}", :framework)
VCAP::Stager::PluginRegistry.register_plugin(p)
end
orch = VCAP::Stager::PluginOrchestrator.new(@tmpdir, @app_props)
expect do
orch.run_plugins
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::Stager::PluginRegistry.register_plugin(p)
orch = VCAP::Stager::PluginOrchestrator.new(@tmpdir, @app_props)
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|
p = create_mock_plugin("plugin_#{ii}", ptype)
p.should_receive(:stage).with(any_args())
VCAP::Stager::PluginRegistry.register_plugin(p)
end
orch = VCAP::Stager::PluginOrchestrator.new(@tmpdir, @app_props)
orch.run_plugins
end
end

def create_mock_plugin(name, type)
ret = mock(name)
ret.stub(:plugin_type).and_return(type)
ret.stub(:name).and_return(name)
ret
end
end

0 comments on commit 22ef9dc

Please sign in to comment.