Permalink
Browse files

Make gem installation see dependent gems

- Add GEM_PATH to gems installation

- Install dependent gems first

- Give access to app's gem path for secure user during gem install

- Try to fetch platform specific gem first for faster install

Change-Id: I4f39f6eea3678e6647d0801095c728e869312a72
  • Loading branch information...
1 parent bea9997 commit 40b01e9b9eb36185cb3029da93386e116105b0f8 @mariash mariash committed Oct 8, 2012
@@ -41,8 +41,8 @@ def build_spec_list(dependencies, locked_specs, specs)
locked_specs.each do |spec|
if dependency_names.include? spec.name
if !specs.include? spec
- specs << spec
build_spec_list(spec.dependencies, locked_specs, specs)
+ specs << spec
end
end
end
@@ -28,10 +28,12 @@ def compile_gems
safe_env << " PATH='#{path}'"
safe_env << " LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8"
- base_dir = StagingPlugin.platform_config["cache"]
+ safe_env << " GEM_PATH='%GEM_PATH%'"
ruby_cmd = "env -i #{safe_env} #{ruby}"
+ base_dir = StagingPlugin.platform_config["cache"]
+
@task = GemfileTask.new(app_dir, library_version, ruby_cmd, base_dir,
{:bundle_without=>bundle_without}, @staging_uid, @staging_gid)
@@ -15,7 +15,7 @@ def initialize(app_dir, library_version, ruby_cmd, base_dir, options={}, uid=nil
@blessed_gems_dir = File.join(@cache_base_dir, "blessed_gems")
FileUtils.mkdir_p(@blessed_gems_dir)
- @ruby_cmd = ruby_cmd
+ @ruby_cmd = ruby_cmd.gsub("%GEM_PATH%", installation_directory)
@uid = uid
@gid = gid
@options = options
@@ -54,7 +54,7 @@ def specs
end
def install
- install_specs(specs)
+ install_specs(specs)
end
def remove_gems_cached_in_app
@@ -113,7 +113,7 @@ def install_gem(name, version)
else
@logger.info("Need to fetch #{gem_filename} from RubyGems")
Dir.mktmpdir do |tmp_dir|
- fetched_path = fetch_gem_from_rubygems(name, version, tmp_dir)
+ fetched_path = get_gem_from_rubygems(name, version, tmp_dir)
installed_path = install_gem_from_path(gem_filename, fetched_path, "fetched")
save_blessed_gem(fetched_path)
end
@@ -235,24 +235,34 @@ def installation_directory
File.join(@app_dir, "rubygems", "ruby", @library_version)
end
- def fetch_gem_from_rubygems(name, version, directory)
- url = rubygems_url_for(name, version)
- gem_filename = gem_filename(name, version)
- cmd = "wget --quiet --retry-connrefused --connect-timeout=5 --no-check-certificate #{url}"
-
+ def get_gem_from_rubygems(name, version, directory)
+ # Try to fetch platform specific gem first
+ gem_filename = gem_filename_platform(name, version)
Dir.chdir(directory) do
- raise "Failed fetching missing gem #{gem_filename} from Rubygems" unless system(cmd)
+ unless fetch_gem_from_rubygems(gem_filename)
+
+ gem_filename = gem_filename(name, version)
+ unless fetch_gem_from_rubygems(gem_filename)
+ raise "Failed fetching missing gem #{gem_filename} from Rubygems"
+ end
+ end
end
File.join(directory, gem_filename)
end
+ def fetch_gem_from_rubygems(gem_filename)
+ url = "http://production.s3.rubygems.org/gems/#{gem_filename}"
+ cmd = "wget --quiet --retry-connrefused --connect-timeout=5 --no-check-certificate #{url}"
+ system(cmd)
+ end
+
def gem_filename(name, version)
"%s-%s.gem" % [ name, version ]
end
- def rubygems_url_for(name, version)
- "http://production.s3.rubygems.org/gems/#{name}-#{version}.gem"
+ def gem_filename_platform(name, version)
+ "%s-%s-%s.gem" % [ name, version, Gem::Platform.local.to_s ]
end
# Stage the gemfile in a temporary directory that is readable by a secure user
@@ -301,7 +311,16 @@ def compile_gem(gemfile_path)
@logger.debug("Doing a gem install from #{staged_gemfile} into #{gem_install_dir} as user #{@uid || 'cc'}")
staging_cmd = "#{@ruby_cmd} -S gem install #{staged_gemfile} --local --no-rdoc --no-ri -E -w -f --ignore-dependencies --install-dir #{gem_install_dir}"
- exitstatus, _ = run_secure(staging_cmd, tmp_dir)
- exitstatus == 0 ? gem_install_dir : nil
+
+ begin
+ # Give access to gem path for gem installation
+ app_staged_dir = File.dirname(File.dirname(@app_dir))
+ secure_chown(app_staged_dir)
+
+ exitstatus, _ = run_secure(staging_cmd, tmp_dir)
+ return exitstatus == 0 ? gem_install_dir : nil
+ ensure
+ unsecure_file(app_staged_dir)
+ end
end
end
@@ -47,11 +47,21 @@ def run_secure(cmd, where, options={})
# Change permissions and ownership of specified file
# to secure user, if @uid is set
def secure_file(file)
+ secure_chmod(file)
+ secure_chown(file)
+ end
+
+ def secure_chmod(file)
if @uid
chmod_output = `/bin/chmod -R 0755 #{file} 2>&1`
if $?.exitstatus != 0
raise "Failed chmodding dir: #{chmod_output}"
end
+ end
+ end
+
+ def secure_chown(file)
+ if @uid
chown_user = @gid ? "#{@uid}:#{@gid}" : @uid
chown_output = `sudo /bin/chown -R #{chown_user} #{file} 2>&1`
if $?.exitstatus != 0
@@ -0,0 +1,4 @@
+source :rubygems
+
+gem "sinatra"
+gem "therubyracer"
@@ -0,0 +1,21 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ libv8 (3.3.10.4)
+ rack (1.4.1)
+ rack-protection (1.2.0)
+ rack
+ sinatra (1.3.3)
+ rack (~> 1.3, >= 1.3.6)
+ rack-protection (~> 1.2)
+ tilt (~> 1.3, >= 1.3.3)
+ therubyracer (0.10.2)
+ libv8 (~> 3.3.10)
+ tilt (1.3.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ sinatra
+ therubyracer
@@ -0,0 +1,9 @@
+require "sinatra"
+require "v8"
+
+cxt = V8::Context.new
+
+get "/" do
+ cxt['foo'] = "Hello world!"
+ cxt.eval('foo')
+end
@@ -255,6 +255,25 @@
end
end
end
+
+describe "An app being staged with gems that depend on other gems" do
+ before do
+ app_fixture :sinatra_dep_gems
+ end
+
+ it "installs all gems" do
+ stage sinatra_staging_env.merge({:runtime_info => {:name => "ruby19", :version => "1.9.2p180",
+ :description => "Ruby 1.9.2", :executable => "ruby"}}) do |staged_dir|
+ platform_specific_gem = "libv8-3.3.10.4-#{Gem::Platform.local.to_s}"
+ Dir.chdir(File.join(staged_dir,'app', 'rubygems', 'ruby', '1.9.1','gems')) do
+ Dir.glob('*').sort.should == ["bundler-1.1.3", "cf-autoconfig-0.0.4", "cf-runtime-0.0.2",
+ platform_specific_gem, "rack-1.4.1", "rack-protection-1.2.0", "sinatra-1.3.3",
+ "therubyracer-0.10.2", "tilt-1.3.3"]
+ end
+ end
+ end
+end
+
def sinatra_staging_env
{:runtime_info => {
:name => "ruby18",
@@ -17,6 +17,29 @@
FileUtils.rm_rf(@git_working_dir)
end
+ describe "Sinatra app with Gemfile.lock" do
+ before :each do
+ test_app = app_fixture_base_directory.join("sinatra_gemfile", "source")
+ FileUtils.cp_r(File.join(test_app, "."), @app_dir)
+ @task = GemfileTask.new(@app_dir, "1.9.1", @ruby_cmd, @working_dir)
+ end
+
+ it "should return specs in the order where dependent gems go first" do
+ # rack and tilt should go before sinatra
+ sinatra_deps = ["rack", "tilt"]
+ found_deps = []
+ @task.specs.each do |spec|
+ found_deps << spec[:name] if sinatra_deps.include?(spec[:name])
+ if spec[:name] == "sinatra"
+ sinatra_deps.each do |dep|
+ found_deps.should include dep
+ end
+ end
+ end
+ end
+
+ end
+
describe "Sinatra app with git dependencies" do
before :each do
test_app = app_fixture_base_directory.join("sinatra_git", "source")

0 comments on commit 40b01e9

Please sign in to comment.