This repository has been archived by the owner on Jan 26, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 351
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge "Scaffolding for staging plugins" into staging_plugin
- Loading branch information
Showing
7 changed files
with
305 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |