Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Begin porting the existing sinatra plugin over to the new plugin fram…

…ework

Creates a starting point for porting the sinatra plugin over to the new
staging plugin framework. This is a very minimal implementation, but will
generate the necessary startup/stop files. The next step is adding support
for gems.

Test plan:
- Unit tests!
- Used this plugin, along with the plugin orchestrator to stage a simple
  sinatra app. Was able to start/stop it.

Change-Id: Ic4c755708ae9e4fa83ba8182ffb777b1a9d301d6
  • Loading branch information...
commit 9b6555a5be6bf56730d20e90d66a5cb1654b73f5 1 parent f8e33f2
mpage authored
View
5 plugins/staging/sinatra/.gitignore
@@ -0,0 +1,5 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
+test/*
View
3  plugins/staging/sinatra/Gemfile
@@ -0,0 +1,3 @@
+source :rubygems
+
+gemspec
View
16 plugins/staging/sinatra/Rakefile
@@ -0,0 +1,16 @@
+require 'rubygems'
+require 'bundler/setup'
+require 'rake'
+
+
+task :spec => ['bundler:install'] do
+ desc 'Run spec tests'
+
+ sh('cd spec && rake spec')
+end
+
+namespace 'bundler' do
+ task 'install' do
+ sh('bundle install')
+ end
+end
View
18 plugins/staging/sinatra/assets/start.erb
@@ -0,0 +1,18 @@
+echo $$ > ./run.pid
+
+<% if uses_bundler %>
+
+export PATH="$PWD/app/rubygems/ruby/<%= library_version %>/bin:$PATH"
+export GEM_PATH="$PWD/app/rubygems/ruby/<%= library_version %>"
+export GEM_HOME="$PWD/app/rubygems/ruby/<%= library_version %>"
+export RUBYOPT="-I$PWD/ruby -rstdsync'"
+cd app
+exec ruby ./rubygems/ruby/<%= library_version %>/bin/bundle exec ruby ./<%= sinatra_main %> $@
+
+<% else %>
+
+export RUBYOPT="-rubygems -I$PWD/ruby -rstdsync"
+cd app
+exec ruby ./<%= sinatra_main %> $@
+
+<% end %>
View
1  plugins/staging/sinatra/assets/stdsync.rb
@@ -0,0 +1 @@
+$stdout.sync = true
View
2  plugins/staging/sinatra/assets/stop
@@ -0,0 +1,2 @@
+head -n 1 ./run.pid | xargs kill -9
+rm -f ./run.pid
View
2  plugins/staging/sinatra/lib/vcap/plugins/staging/sinatra_staging_plugin.rb
@@ -0,0 +1,2 @@
+require 'vcap/plugins/staging/sinatra_staging_plugin/plugin'
+require 'vcap/plugins/staging/sinatra_staging_plugin/version'
View
75 plugins/staging/sinatra/lib/vcap/plugins/staging/sinatra_staging_plugin/plugin.rb
@@ -0,0 +1,75 @@
+require 'erb'
+require 'fileutils'
+
+module VCAP
+ module Plugins
+ module Staging
+ class SinatraStagingPluginError < StandardError; end
+ end
+ end
+end
+
+class VCAP::Plugins::Staging::SinatraStagingPlugin
+ ASSET_DIR = File.expand_path('../../../../../../assets', __FILE__)
+ STDSYNC_PATH = File.join(ASSET_DIR, 'stdsync.rb')
+ START_TEMPLATE_PATH = File.join(ASSET_DIR, 'start.erb')
+ SINATRA_DETECTION_REGEX = /require 'sinatra'|require "sinatra"/
+
+ attr_reader :name, :plugin_type
+
+ def initialize
+ @name = 'vcap_sinatra_plugin'
+ @plugin_type = :framework
+ end
+
+ def stage(app_dir, actions, app_properties)
+ sinatra_main = find_main_file(app_dir)
+ raise VCAP::Plugins::Staging::SinatraStagingPluginError, "Couldn't find main file" unless sinatra_main
+ copy_stdsync(actions.droplet_base_dir)
+ generate_start_script(actions.start_script, app_dir, sinatra_main, app_properties)
+ end
+
+ private
+
+ # Finds the first file in the application root that requires sinatra.
+ #
+ # @param app_dir Path to the root of the application's source directory.
+ def find_main_file(app_dir)
+ for src_file in Dir.glob(File.join(app_dir, '*'))
+ src_contents = File.read(src_file)
+ return File.basename(src_file) if src_contents.match(SINATRA_DETECTION_REGEX)
+ end
+ nil
+ end
+
+ # The file stdsync.rb contains a single line that sets stdout to synchronous mode.
+ # The startup script we generate expects it to live at 'DROPLET_ROOT/ruby/stdsync.rb'.
+ # Make sure we put it there.
+ #
+ # @param base_dir String Path to the base of the droplet being created.
+ def copy_stdsync(base_dir)
+ stdsync_dir = File.join(base_dir, 'ruby')
+ stdsync_dst = File.join(stdsync_dir, 'stdsync.rb')
+ FileUtils.mkdir(stdsync_dir)
+ FileUtils.cp(STDSYNC_PATH, stdsync_dst)
+ end
+
+ # Generates the startup script that will be run on the DEA.
+ #
+ # @param start_script File Open file that will house the start script.
+ # @param source_dir String Path to application source
+ # @param sinatra_main String Name of 'main' sinatra file.
+ # @param app_props VCAP::Stager::AppProperties Properties of app being staged.
+ def generate_start_script(start_script, source_dir, sinatra_main, app_props)
+ # Template vars
+ if app_props.runtime == 'ruby19'
+ library_version = '1.9.1'
+ else
+ library_version = '1.8'
+ end
+ uses_bundler = File.exists?(File.join(source_dir, 'Gemfile.lock'))
+
+ template = ERB.new(File.read(START_TEMPLATE_PATH))
+ start_script.write(template.result(binding))
+ end
+end
View
9 plugins/staging/sinatra/lib/vcap/plugins/staging/sinatra_staging_plugin/version.rb
@@ -0,0 +1,9 @@
+module VCAP
+ module Plugins
+ module Staging
+ class SinatraStagingPlugin
+ VERSION = '0.0.1'
+ end
+ end
+ end
+end
View
13 plugins/staging/sinatra/spec/Rakefile
@@ -0,0 +1,13 @@
+BASE_DIR = File.expand_path(File.join('..', '..'), __FILE__)
+ENV["BUNDLE_GEMFILE"] ||= File.join(BASE_DIR, 'Gemfile')
+require 'rubygems'
+require 'bundler/setup'
+
+require 'rake'
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new(:spec) do |t|
+ t.pattern = '**/*_spec.rb'
+ t.rspec_opts = ['--color', '--format nested']
+end
+task :default => [:spec]
View
95 plugins/staging/sinatra/spec/plugin_spec.rb
@@ -0,0 +1,95 @@
+$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
+require 'fileutils'
+require 'tmpdir'
+
+require 'vcap/plugins/staging/sinatra_staging_plugin'
+
+describe VCAP::Plugins::Staging::SinatraStagingPlugin do
+ TEST_ASSETS_DIR = File.expand_path('../../test_assets', __FILE__)
+
+ describe '#find_main_file' do
+ before :each do
+ @tmpdir = Dir.mktmpdir
+ end
+
+ after :each do
+ FileUtils.rm_rf(@tmpdir)
+ end
+
+ it 'should return nil if it cannot find a ruby file in the root directory that requires sinatra' do
+ plugin = VCAP::Plugins::Staging::SinatraStagingPlugin.new
+ plugin.send(:find_main_file, @tmpdir).should be_nil
+ end
+
+ it 'should detect files that require sinatra using single quotes' do
+ copy_test_asset('sinatra_single_quotes.rb', @tmpdir)
+ plugin = VCAP::Plugins::Staging::SinatraStagingPlugin.new
+ plugin.send(:find_main_file, @tmpdir).should == 'sinatra_single_quotes.rb'
+ end
+
+ it 'should detect files that require sinatra using double quotes' do
+ copy_test_asset('sinatra_double_quotes.rb', @tmpdir)
+ plugin = VCAP::Plugins::Staging::SinatraStagingPlugin.new
+ plugin.send(:find_main_file, @tmpdir).should == 'sinatra_double_quotes.rb'
+ end
+ end
+
+ describe '#copy_stdsync' do
+ before :each do
+ @tmpdir = Dir.mktmpdir
+ end
+
+ after :each do
+ FileUtils.rm_rf(@tmpdir)
+ end
+
+ it 'should copy stdsync.rb to ruby/stdsync.rb in the droplet root' do
+ plugin = VCAP::Plugins::Staging::SinatraStagingPlugin.new
+ plugin.send(:copy_stdsync, @tmpdir)
+ File.exist?(File.join(@tmpdir, 'ruby', 'stdsync.rb')).should be_true
+ end
+ end
+
+ describe '#generate_start_script' do
+ before :each do
+ @tmpdir = Dir.mktmpdir
+ end
+
+ after :each do
+ FileUtils.rm_rf(@tmpdir)
+ end
+
+ # Not sure about the best way to test this. We're really just checking that
+ # the correct block of the if statement is executed in the startup script template...
+ it 'should use bundle to start the app if it includes a Gemfile.lock' do
+ copy_test_asset('sinatra_single_quotes.rb', @tmpdir)
+ File.open(File.join(@tmpdir, 'Gemfile.lock'), 'w+') {|f| f.write('foo') }
+ plugin = VCAP::Plugins::Staging::SinatraStagingPlugin.new
+ start_script = StringIO.new
+ props = mock(:props)
+ props.should_receive(:runtime).and_return('ruby19')
+ plugin.send(:generate_start_script, start_script, @tmpdir, 'sinatra_single_quotes.rb', props)
+ start_script.rewind
+ script_contents = start_script.read
+ script_contents.match('bundle exec').should be_true
+ end
+
+ it 'start the app directly if no Gemfile.lock is included' do
+ copy_test_asset('sinatra_single_quotes.rb', @tmpdir)
+ plugin = VCAP::Plugins::Staging::SinatraStagingPlugin.new
+ start_script = StringIO.new
+ props = mock(:props)
+ props.should_receive(:runtime).and_return('ruby19')
+ plugin.send(:generate_start_script, start_script, @tmpdir, 'sinatra_single_quotes.rb', props)
+ start_script.rewind
+ script_contents = start_script.read
+ script_contents.match('bundle exec').should be_false
+ end
+ end
+
+ def copy_test_asset(asset_name, dst_dir)
+ src = File.join(TEST_ASSETS_DIR, asset_name)
+ dst = File.join(@tmpdir, asset_name)
+ FileUtils.cp(src, dst)
+ end
+end
View
5 plugins/staging/sinatra/test_assets/sinatra_double_quotes.rb
@@ -0,0 +1,5 @@
+require 'sinatra'
+
+get '/' do
+ "HELLO WORLD!"
+end
View
5 plugins/staging/sinatra/test_assets/sinatra_single_quotes.rb
@@ -0,0 +1,5 @@
+require 'sinatra'
+
+get '/' do
+ "HELLO WORLD!"
+end
View
26 plugins/staging/sinatra/vcap_sinatra_staging_plugin.gemspec
@@ -0,0 +1,26 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "vcap/plugins/staging/sinatra_staging_plugin/version"
+
+Gem::Specification.new do |s|
+ s.name = "vcap_sinatra_staging_plugin"
+ s.version = VCAP::Plugins::Staging::SinatraStagingPlugin::VERSION
+ s.authors = ["mpage"]
+ s.email = ["mpage@vmware.com"]
+ s.homepage = "http://www.cloudfoundry.com"
+ s.summary = %q{Stages sinatra applications for deployment in Cloud Foundry.}
+ s.description = %q{Modifies applications for deployment in CF.}
+
+ s.rubyforge_project = "vcap_sinatra_staging_plugin"
+
+ s.files = %w(Rakefile Gemfile) + Dir.glob("{lib,spec,assets,test_assets}/**/*")
+ s.executables = []
+ s.bindir = 'bin'
+ s.require_paths = ["lib"]
+
+ # XXX - These need to be development dependencies. Figure out why bundler isn't installing
+ # them later...
+ s.add_dependency("rspec")
+ s.add_dependency("rspec-core")
+ s.add_dependency("rake")
+end
Please sign in to comment.
Something went wrong with that request. Please try again.