diff --git a/.kitchen.yml b/.kitchen.yml index 8cdcf7a..9384063 100644 --- a/.kitchen.yml +++ b/.kitchen.yml @@ -7,6 +7,7 @@ provisioner: platforms: - name: ubuntu-12.04 + - name: ubuntu-14.04 - name: debian-7.5 - name: centos-6.5 - name: amazon @@ -34,3 +35,23 @@ suites: run_list: - recipe[ruby_install] - recipe[partial_ruby_version] +- name: user_ruby + run_list: + - recipe[ruby_install] + - recipe[user_ruby] + attributes: + user_ruby: + user: vagrant +- name: patch_ruby + run_list: + - recipe[ruby_install] + - recipe[user_ruby] + excludes: + - ubuntu-12.04 + - debian-7.5 + - centos-6.5 + - amazon + attributes: + user_ruby: + rubie: ruby 2.0.0-p451 + patch: https://gist.githubusercontent.com/plexus/10021261/raw/305492ebd17308e55eee1baab27568fafaa940cb/ruby-2.0-p451-readline.patch diff --git a/Berksfile b/Berksfile index d376386..a7384b9 100644 --- a/Berksfile +++ b/Berksfile @@ -4,4 +4,5 @@ metadata group :integration do cookbook 'alltherubies', path: './test/cookbooks/alltherubies' cookbook 'partial_ruby_version', path: './test/cookbooks/partial_ruby_version' + cookbook 'user_ruby', path: './test/cookbooks/user_ruby' end diff --git a/providers/ruby.rb b/providers/ruby.rb index 5cad6de..7e6b326 100644 --- a/providers/ruby.rb +++ b/providers/ruby.rb @@ -46,10 +46,7 @@ def perform_install install_start = Time.now Chef::Log.info("Building ruby_install_ruby[#{@rubie}], this could take a while...") - rubie = @rubie # bypass block scoping issue - prefix_path = @prefix_path # bypass block scoping issue - - execute_ruby_install(rubie, prefix_path) + execute_ruby_install Chef::Log.info("ruby_install_ruby[#{@rubie}] build time was " \ "#{(Time.now - install_start) / 60.0} minutes") @@ -57,9 +54,10 @@ def perform_install end end -def execute_ruby_install(rubie, prefix_path) +def execute_ruby_install + rubie, opts = @rubie, options # avoid variable scoping issues execute "ruby-install[#{rubie}]" do - command %( /usr/local/bin/ruby-install --rubies-dir "#{prefix_path}" "#{rubie}" ) + command %(/usr/local/bin/ruby-install #{opts.join(' ')} "#{rubie}") user new_resource.user if new_resource.user group new_resource.group if new_resource.group environment new_resource.environment if new_resource.environment @@ -72,6 +70,19 @@ def ruby_installed? false else install_dirname = @rubie.sub(' ', '-') - ::File.exists?("#{@prefix_path}/#{install_dirname}/bin/ruby") + ::File.exist?("#{@prefix_path}/#{install_dirname}/bin/ruby") + end +end + +def options # rubocop: disable CyclomaticComplexity + opt = [%(--rubies-dir "#{@prefix_path}")] + opt << %(--no-install-deps) unless new_resource.install_deps + opt << %(--src-dir "#{new_resource.source_dir}") if new_resource.source_dir + opt << %(--mirror "#{new_resource.mirror}") if new_resource.mirror + opt << %(--url "#{new_resource.url}") if new_resource.url + opt << %(--md5 "#{new_resource.md5}") if new_resource.md5 + if new_resource.patch + opt << Array(new_resource.patch).map { |p| %(--patch "#{p}") } end + opt end diff --git a/resources/ruby.rb b/resources/ruby.rb index d59c7d4..cec1d8b 100644 --- a/resources/ruby.rb +++ b/resources/ruby.rb @@ -23,11 +23,17 @@ actions :install, :reinstall -attribute :definition, kind_of: String, name_attribute: true -attribute :prefix_path, kind_of: String -attribute :user, kind_of: String -attribute :group, kind_of: String -attribute :environment, kind_of: Hash +attribute :definition, kind_of: String, name_attribute: true +attribute :prefix_path, kind_of: String +attribute :source_dir, kind_of: String +attribute :patch, kind_of: [String, Array] +attribute :mirror, kind_of: String +attribute :url, kind_of: String +attribute :md5, kind_of: String +attribute :install_deps, kind_of: [TrueClass, FalseClass], default: true +attribute :user, kind_of: String +attribute :group, kind_of: String +attribute :environment, kind_of: Hash def initialize(*args) super diff --git a/test/cookbooks/user_ruby/attributes/default.rb b/test/cookbooks/user_ruby/attributes/default.rb new file mode 100644 index 0000000..df16a45 --- /dev/null +++ b/test/cookbooks/user_ruby/attributes/default.rb @@ -0,0 +1,4 @@ +default['user_ruby']['user'] = nil +default['user_ruby']['group'] = nil +default['user_ruby']['rubie'] = 'ruby 1.9.3-p545' +default['user_ruby']['patch'] = nil diff --git a/test/cookbooks/user_ruby/metadata.rb b/test/cookbooks/user_ruby/metadata.rb new file mode 100644 index 0000000..ac9a331 --- /dev/null +++ b/test/cookbooks/user_ruby/metadata.rb @@ -0,0 +1,8 @@ +# -*- encoding : utf-8 -*- +name 'user_ruby' +maintainer 'Ross Timson' +maintainer_email 'ross@rosstimson.com' +license 'Apache 2.0' +description 'Installs latest Ruby 1.9 as user via ruby_install cookbook' +long_description 'Installs latest Ruby 1.9 as user via ruby_install cookbook' +version '0.1.0' diff --git a/test/cookbooks/user_ruby/recipes/default.rb b/test/cookbooks/user_ruby/recipes/default.rb new file mode 100644 index 0000000..556ec0c --- /dev/null +++ b/test/cookbooks/user_ruby/recipes/default.rb @@ -0,0 +1,54 @@ +# -*- encoding : utf-8 -*- + +# When passing non-root user to LWRP, ruby-install may fail when attempting +# to install dependencies due to privelges of execution block. Setting the +# 'install_deps' flag to false in this case will run ruby-install +# with --no-install-deps. +# +# ruby-install builds from ~/src when non-root and /usr/loca/src when root, so +# passing $HOME to environment will allow build to succeed when +# executing as non-root. + + +require 'etc' + +# Will LWRP execute as non-root user? +user = node['user_ruby']['user'] +is_root = user && Etc.getpwnam(user)['uid'] == 0 || user.nil? + +# Group attribute setting trumps all, but get user's group if not specified. +# Otherwise, files could be owned by user:root rather than user:user +group = node['user_ruby']['group'] +group ||= Etc.getgrgid(Etc.getpwnam(user)['gid'])['name'] if user + +# Use when building prefix_path (if not othwerwise specified), and to +# pass as $HOME env variable if running as non-user (helps ruby-install) +home_dir = Etc.getpwnam(user)['dir'] if user && !is_root + +# Install deps manually if LWRP will be executing as non-root, otherwise +# execution block of LWRP will not ha +unless is_root + case node[:platform_family] + when 'debian' + packages = %w(build-essential zlib1g-dev libyaml-dev libssl-dev + libgdbm-dev libreadline-dev libncurses5-dev libffi-dev) + when 'fedora', 'rhel' + packages = %w(gcc automake zlib-devel libyaml-devel openssl-devel + gdbm-devel readline-devel ncurses-devel libffi-devel) + end + + packages.each do |p| + package p do + action :install + end + end +end + +ruby_install_ruby node['user_ruby']['rubie'] do + user user if user + group group if group + environment 'HOME' => home_dir if home_dir + prefix_path File.join(home_dir, '.rubies') if home_dir + install_deps false unless is_root + patch node['user_ruby']['patch'] +end diff --git a/test/integration/patch_ruby/serverspec/ruby_patch_spec.rb b/test/integration/patch_ruby/serverspec/ruby_patch_spec.rb new file mode 100644 index 0000000..264cfc5 --- /dev/null +++ b/test/integration/patch_ruby/serverspec/ruby_patch_spec.rb @@ -0,0 +1,16 @@ +# -*- encoding : utf-8 -*- + +require_relative 'spec_helper' + +describe 'compiles and installs patched Ruby 2.0.0-p451 into /opt/rubies' do + describe command('/opt/rubies/ruby-2.0.0-p451/bin/ruby -v') do + it { should return_exit_status 0 } + it { should return_stdout(/ruby 2.0.0p451.*/) } + end + + describe file('/opt/rubies/ruby-2.0.0-p451') do + it { should be_directory } + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + end +end diff --git a/test/integration/patch_ruby/serverspec/spec_helper.rb b/test/integration/patch_ruby/serverspec/spec_helper.rb new file mode 100644 index 0000000..22e49ca --- /dev/null +++ b/test/integration/patch_ruby/serverspec/spec_helper.rb @@ -0,0 +1,12 @@ +# -*- encoding : utf-8 -*- + +require 'serverspec' + +include Serverspec::Helper::Exec +include Serverspec::Helper::DetectOS + +RSpec.configure do |c| + c.before :all do + c.path = '/sbin:/usr/bin' + end +end diff --git a/test/integration/user_ruby/serverspec/spec_helper.rb b/test/integration/user_ruby/serverspec/spec_helper.rb new file mode 100644 index 0000000..22e49ca --- /dev/null +++ b/test/integration/user_ruby/serverspec/spec_helper.rb @@ -0,0 +1,12 @@ +# -*- encoding : utf-8 -*- + +require 'serverspec' + +include Serverspec::Helper::Exec +include Serverspec::Helper::DetectOS + +RSpec.configure do |c| + c.before :all do + c.path = '/sbin:/usr/bin' + end +end diff --git a/test/integration/user_ruby/serverspec/user_ruby_spec.rb b/test/integration/user_ruby/serverspec/user_ruby_spec.rb new file mode 100644 index 0000000..a6c2198 --- /dev/null +++ b/test/integration/user_ruby/serverspec/user_ruby_spec.rb @@ -0,0 +1,16 @@ +# -*- encoding : utf-8 -*- + +require_relative 'spec_helper' + +describe 'compiles and installs Ruby 1.9.3-p545 into /home/vagrant/.rubies' do + describe command('/home/vagrant/.rubies/ruby-1.9.3-p545/bin/ruby -v') do + it { should return_exit_status 0 } + it { should return_stdout(/ruby 1.9.3p545.*/) } + end + + describe file('/home/vagrant/.rubies/ruby-1.9.3-p545') do + it { should be_directory } + it { should be_owned_by 'vagrant' } + it { should be_grouped_into 'vagrant' } + end +end