Vendor the external cookbooks

Knife-Zero doesn't include Berkshelf support, so vendoring everything in
the repo is convenient again
This commit is contained in:
Greg Karékinian
2019-10-13 19:17:42 +02:00
parent f4bfe31ac1
commit a32f34b408
1245 changed files with 100630 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module PoiseRuby
autoload :BundlerMixin, 'poise_ruby/bundler_mixin'
autoload :Error, 'poise_ruby/error'
autoload :Resources, 'poise_ruby/resources'
autoload :RubyCommandMixin, 'poise_ruby/ruby_command_mixin'
autoload :RubyProviders, 'poise_ruby/ruby_providers'
autoload :VERSION, 'poise_ruby/version'
end

View File

@@ -0,0 +1,84 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'poise_ruby/error'
module PoiseRuby
# Mixin for creating bundle exec commands.
#
# @since 2.1.0
module BundlerMixin
# Transform a command to run under `bundle exec` with the same semantics as
# Ruby execution elsewhere in this system. That means you should end up with
# something like `/bin/ruby /bin/bundle exec /bin/ruby /bin/cmd args`.
#
# @param cmd [String, Array<String>] Command to transform.
# @param path [String] Optional input path for command resolution.
# @return [String, Array<String>]
def bundle_exec_command(cmd, path: nil)
bundle = new_resource.parent_bundle
return cmd unless bundle
is_array = cmd.is_a?(Array)
cmd = Shellwords.split(cmd) unless is_array
root_path = ::File.expand_path('..', bundle.gemfile_path)
# Grab this once in case I need it for the extra path.
bundler_binary = bundle.bundler_binary
# This doesn't account for the potential of a .bundle/config created with
# settings that Chef doesn't know about. (╯°□°)╯︵ ┻━┻
extra_path = if bundle.binstubs
bundle.binstubs == true ? 'bin' : bundle.binstubs
elsif bundle.vendor || bundle.deployment
# Find the relative path to start searching from.
vendor_base_path = if bundle.vendor && bundle.vendor != true
bundle.vendor
else
'vendor/bundle'
end
# Add the ruby/.
vendor_base_path = ::File.join(File.expand_path(vendor_base_path, root_path), 'ruby')
# Find the version number folder inside that.
candidates = Dir.entries(vendor_base_path)
ruby_abi_folder = candidates.find {|name| name =~ /^\d\./ }
vendor_sub_path = if ruby_abi_folder
::File.join(ruby_abi_folder, 'bin')
elsif candidates.include?('bin')
'bin'
else
raise PoiseRuby::Error.new("Unable to find the vendor bin folder for #{vendor_base_path}: #{candidates.join(', ')}")
end
# Make the final path.
::File.join(vendor_base_path, vendor_sub_path)
else
# The folder the bundler binary is in was the global gem executable dir.
::File.dirname(bundler_binary)
end
# Resolve relative paths against Bundler.root.
extra_path = ::File.expand_path(extra_path, root_path)
# Create the full $PATH.
path ||= ENV['PATH']
bundle_exec_path = extra_path + ::File::PATH_SEPARATOR + path
# Resolve the command
abs_cmd = PoiseLanguages::Utils.absolute_command(cmd, path: bundle_exec_path)
bundle_exec = [new_resource.ruby, bundler_binary, 'exec', new_resource.ruby] + abs_cmd
if is_array
bundle_exec
else
PoiseLanguages::Utils.shelljoin(bundle_exec)
end
end
end
end

View File

@@ -0,0 +1,18 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'poise_ruby/resources'
require 'poise_ruby/ruby_providers'

View File

@@ -0,0 +1,21 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module PoiseRuby
class Error < ::Exception
end
end

View File

@@ -0,0 +1,29 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'poise_ruby/resources/bundle_install'
require 'poise_ruby/resources/ruby_execute'
require 'poise_ruby/resources/ruby_gem'
require 'poise_ruby/resources/ruby_runtime'
module PoiseRuby
# Chef resources and providers for poise-ruby.
#
# @since 2.0.0
module Resources
end
end

View File

@@ -0,0 +1,225 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/mixin/shell_out'
require 'chef/mixin/which'
require 'chef/provider'
require 'chef/resource'
require 'poise'
require 'poise_ruby/error'
require 'poise_ruby/ruby_command_mixin'
module PoiseRuby
module Resources
# (see BundleInstall::Resource)
# @since 2.0.0
module BundleInstall
# A `bundle_install` resource to install a [Bundler](http://bundler.io/)
# Gemfile.
#
# @provides bundle_install
# @action install
# @action update
# @note
# This resource is not idempotent itself, it will always run `bundle
# install`.
# @example
# bundle_install '/opt/my_app' do
# gem_path '/usr/local/bin/gem'
# end
class Resource < Chef::Resource
include Poise
provides(:bundle_install)
actions(:install, :update)
include PoiseRuby::RubyCommandMixin
# @!attribute path
# Path to the Gemfile or to a directory that contains a Gemfile.
# @return [String]
attribute(:path, kind_of: String, name_attribute: true)
# @!attribute binstubs
# Enable binstubs. If set to a string it is the path to generate
# stubs in.
# @return [Boolean, String]
attribute(:binstubs, kind_of: [TrueClass, String])
# @!attribute deployment
# Enable deployment mode.
# @return [Boolean]
attribute(:deployment, equal_to: [true, false], default: false)
# @!attribute jobs
# Number of parallel installations to run.
# @return [String, Integer]
attribute(:jobs, kind_of: [String, Integer])
# @!attribute retry
# Number of times to retry failed installations.
# @return [String, Integer]
attribute(:retry, kind_of: [String, Integer])
# @!attribute user
# User to run bundler as.
# @return [String, Integery, nil]
attribute(:user, kind_of: [String, Integer, NilClass])
# @!attribute vendor
# Enable local vendoring. This maps to the `--path` option in bundler,
# but that attribute name is already used.
# @return [Boolean, String]
attribute(:vendor, kind_of: [TrueClass, String])
# @!attribute without
# Group or groups to not install.
# @return [String, Array<String>]
attribute(:without, kind_of: [Array, String])
# The path to the `bundle` binary for this installation. This is an
# output property.
#
# @return [String]
# @example
# execute "#{resources('bundle_install[/opt/myapp]').bundler_binary} vendor"
def bundler_binary
@bundler_binary ||= provider_for_action(:bundler_binary).bundler_binary
end
# The path to the Gemfile for this installation. This is an output
# property.
#
# @return [String]
# @example
# file resources('bundle_install[/opt/myapp]').gemfile_path do
# owner 'root'
# end
def gemfile_path
@gemfile_path ||= provider_for_action(:gemfile_path).gemfile_path
end
end
# The default provider for the `bundle_install` resource.
#
# @see Resource
class Provider < Chef::Provider
include Poise
provides(:bundle_install)
include PoiseRuby::RubyCommandMixin
# Install bundler and the gems in the Gemfile.
def action_install
run_bundler('install')
end
# Install bundler and update the gems in the Gemfile.
def action_update
run_bundler('update')
end
# Return the absolute path to the correct bundle binary to run.
#
# @return [String]
def bundler_binary
@bundler_binary ||= ::File.join(poise_gem_bindir, 'bundle')
end
# Find the absolute path to the Gemfile. This mirrors bundler's internal
# search logic by scanning up to parent folder as needed.
#
# @return [String]
def gemfile_path
@gemfile_path ||= begin
path = ::File.expand_path(new_resource.path)
if ::File.file?(path)
# We got a path to a real file, use that.
path
else
# Walk back until path==dirname(path) meaning we are at the root
while path != (next_path = ::File.dirname(path))
possible_path = ::File.join(path, 'Gemfile')
return possible_path if ::File.file?(possible_path)
path = next_path
end
end
end
end
private
# Install the gems in the Gemfile.
def run_bundler(command)
return converge_by "Run bundle #{command}" if whyrun_mode?
cmd = ruby_shell_out!(bundler_command(command), environment: {'BUNDLE_GEMFILE' => gemfile_path}, user: new_resource.user)
# Look for a line like 'Installing $gemname $version' to know if we did anything.
if cmd.stdout.include?('Installing')
new_resource.updated_by_last_action(true)
end
end
# Parse out the value for Gem.bindir. This is so complicated to minimize
# the required configuration on the resource combined with gem having
# terrible output formats.
#
# Renamed from #gem_bindir in 2.3.0 because of a conflict with a method
# of the same name in Chef::Mixin::PathSanity (which is pulled in via
# ShellOut) added in 13.0.
#
# @return [String]
def poise_gem_bindir
cmd = ruby_shell_out!(new_resource.gem_binary, 'environment')
# Parse a line like:
# - EXECUTABLE DIRECTORY: /usr/local/bin
matches = cmd.stdout.scan(/EXECUTABLE DIRECTORY: (.*)$/).first
if matches
matches.first
else
raise PoiseRuby::Error.new("Cannot find EXECUTABLE DIRECTORY: #{cmd.stdout}")
end
end
# Command line options for the bundle install.
#
# @return [Array<String>]
def bundler_options
[].tap do |opts|
if new_resource.binstubs
opts << "--binstubs" + (new_resource.binstubs.is_a?(String) ? "=#{new_resource.binstubs}" : '')
end
if new_resource.vendor
opts << "--path=" + (new_resource.vendor.is_a?(String) ? new_resource.vendor : 'vendor/bundle')
end
if new_resource.deployment
opts << '--deployment'
end
if new_resource.jobs
opts << "--jobs=#{new_resource.jobs}"
end
if new_resource.retry
opts << "--retry=#{new_resource.retry}"
end
if new_resource.without
opts << '--without'
opts.insert(-1, *new_resource.without)
end
end
end
# Command array to run when installing the Gemfile.
#
# @return [Array<String>]
def bundler_command(command)
[bundler_binary, command] + bundler_options
end
end
end
end
end

View File

@@ -0,0 +1,90 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/mash'
require 'chef/provider/execute'
require 'chef/resource/execute'
require 'poise'
require 'poise_ruby/bundler_mixin'
require 'poise_ruby/ruby_command_mixin'
module PoiseRuby
module Resources
# (see RubyExecute::Resource)
# @since 2.0.0
module RubyExecute
# A `ruby_execute` resource to run Ruby scripts and commands.
#
# @provides ruby_execute
# @action run
# @example
# ruby_execute 'myapp.rb' do
# user 'myuser'
# end
class Resource < Chef::Resource::Execute
include Poise
provides(:ruby_execute)
actions(:run)
include PoiseRuby::RubyCommandMixin
# @!attribute parent_bundle
# Optional bundle_install resource to run `bundle exec` against.
# @return [PoiseRuby::Resources::BundleInstall::Resource]
parent_attribute(:bundle, type: :bundle_install, optional: true, auto: false)
end
# The default provider for `ruby_execute`.
#
# @see Resource
# @provides ruby_execute
class Provider < Chef::Provider::Execute
include PoiseRuby::BundlerMixin
provides(:ruby_execute)
private
# Command to pass to shell_out.
#
# @return [String, Array<String>]
def command
if new_resource.parent_bundle
bundle_exec_command(new_resource.command, path: environment['PATH'])
else
if new_resource.command.is_a?(Array)
[new_resource.ruby] + new_resource.command
else
"#{new_resource.ruby} #{new_resource.command}"
end
end
end
# Environment variables to pass to shell_out.
#
# @return [Hash]
def environment
Mash.new.tap do |environment|
environment.update(new_resource.parent_ruby.ruby_environment) if new_resource.parent_ruby
environment['BUNDLE_GEMFILE'] = new_resource.parent_bundle.gemfile_path if new_resource.parent_bundle
environment.update(new_resource.environment) if new_resource.environment
end
end
end
end
end
end

View File

@@ -0,0 +1,125 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/provider/package/rubygems'
require 'chef/resource/gem_package'
require 'poise'
require 'poise_ruby/ruby_command_mixin'
module PoiseRuby
module Resources
# (see RubyGem::Resource)
# @since 2.0.0
module RubyGem
# A `ruby_gem` resource to install Ruby gems.
#
# @provides ruby_gem
# @action install
# @action upgrade
# @action remove
# @action purge
# @action reconfig
# @example
# ruby_gem 'rack'
class Resource < Chef::Resource::GemPackage
include Poise
provides(:ruby_gem)
actions(:install, :upgrade, :remove, :purge, :reconfig)
include PoiseRuby::RubyCommandMixin
# @api private
def initialize(name, run_context=nil)
super
@resource_name = :ruby_gem if @resource_name
# Remove when all useful versions are using provider resolver.
@provider = PoiseRuby::Resources::RubyGem::Provider if @provider
end
end
# The default provider for `ruby_gem`.
#
# @see Resource
# @provides ruby_gem
class Provider < Chef::Provider::Package::Rubygems
include Poise
provides(:ruby_gem)
def load_current_resource
patch_environment { super }
end
def define_resource_requirements
patch_environment { super }
end
def action_install
patch_environment { super }
end
def action_upgrade
patch_environment { super }
end
def action_remove
patch_environment { super }
end
def action_purge
patch_environment { super }
end
def action_reconfig
patch_environment { super }
end
private
def patch_environment(&block)
environment_to_add = if new_resource.parent_ruby
new_resource.parent_ruby.ruby_environment
else
{}
end
begin
if ENV['GEM_HOME'] && !ENV['GEM_HOME'].empty?
Chef::Log.warn("[#{new_resource}] $GEM_HOME is set in Chef's environment, this will likely interfere with gem installation")
end
if ENV['GEM_PATH'] && !ENV['GEM_PATH'].empty?
Chef::Log.warn("[#{new_resource}] $GEM_PATH is set in Chef's environment, this will likely interfere with gem installation")
end
old_vars = environment_to_add.inject({}) do |memo, (key, value)|
memo[key] = ENV[key]
ENV[key] = value
memo
end
block.call
ensure
old_vars.each do |key, value|
if value.nil?
ENV.delete(key)
else
ENV[key] = value
end
end
end
end
end
end
end
end

View File

@@ -0,0 +1,87 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/resource'
require 'poise'
module PoiseRuby
module Resources
# (see RubyRuntime::Resource)
# @since 2.0.0
module RubyRuntime
# A `ruby_runtime` resource to manage Ruby installations.
#
# @provides ruby_runtime
# @action install
# @action uninstall
# @example
# ruby_runtime '2.1.2'
class Resource < Chef::Resource
include Poise(inversion: true, container: true)
provides(:ruby_runtime)
actions(:install, :uninstall)
# @!attribute version
# Version of Ruby to install.
# @return [String]
attribute(:version, kind_of: String, name_attribute: true)
# @!attribute bundler_version
# Version of Bundler to install. It set to `true`, the latest
# available version will be used. If set to `false`, Bundler will
# not be installed.
# @note Disabling the Bundler install may result in other resources
# being non-functional.
# @return [String, Boolean]
attribute(:bundler_version, kind_of: [String, TrueClass, FalseClass], default: true)
# The path to the `ruby` binary for this Ruby installation. This is an
# output property.
#
# @return [String]
# @example
# execute "#{resources('ruby_runtime[2.2.2]').ruby_binary} myapp.rb"
def ruby_binary
@ruby_binary ||= provider_for_action(:ruby_binary).ruby_binary
end
# The environment variables for this Ruby installation. This is an
# output property.
#
# @return [Hash<String, String>]
# @example
# execute '/opt/myapp.py' do
# environment resources('ruby_runtime[2.2.2]').ruby_environment
# end
def ruby_environment
@ruby_environment ||= provider_for_action(:ruby_environment).ruby_environment
end
# The path to the `gem` binary for this Ruby installation. This is an
# output property.
#
# @return [String]
# @example
# execute "#{resources('ruby_runtime[2.2.2]').gem_binary} install myapp"
def gem_binary
@gem_binary ||= provider_for_action(:gem_binary).gem_binary
end
end
# Providers can be found under lib/poise_ruby/ruby_providers/
end
end
end

View File

@@ -0,0 +1,213 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/mixin/convert_to_class_name'
require 'chef/provider'
require 'chef/resource'
require 'poise'
module PoiseRuby
module Resources
# (see RubyRuntimeTest::Resource)
# @since 2.1.0
# @api private
module RubyRuntimeTest
# A `ruby_runtime_test` resource for integration testing of this
# cookbook. This is an internal API and can change at any time.
#
# @provides ruby_runtime_test
# @action run
class Resource < Chef::Resource
include Poise
provides(:ruby_runtime_test)
actions(:run)
attribute(:version, kind_of: String, name_attribute: true)
attribute(:runtime_provider, kind_of: Symbol)
attribute(:path, kind_of: String, default: lazy { default_path })
def default_path
::File.join('', 'root', "ruby_test_#{name}")
end
end
# The default provider for `ruby_runtime_test`.
#
# @see Resource
# @provides ruby_runtime_test
class Provider < Chef::Provider
include Poise
provides(:ruby_runtime_test)
# The `run` action for the `ruby_runtime_test` resource.
#
# @return [void]
def action_run
notifying_block do
# Top level directory for this test.
directory new_resource.path
# Install and log the version.
ruby_runtime new_resource.name do
provider new_resource.runtime_provider if new_resource.runtime_provider
version new_resource.version
end
test_version
# Test ruby_gem.
ruby_gem 'thor remove before' do
action :remove
package_name 'thor'
ruby new_resource.name
end
test_require('thor', 'thor_before')
ruby_gem 'thor' do
ruby new_resource.name
notifies :create, sentinel_file('thor'), :immediately
end
test_require('thor', 'thor_mid')
ruby_gem 'thor again' do
package_name 'thor'
ruby new_resource.name
notifies :create, sentinel_file('thor2'), :immediately
end
ruby_gem 'thor remove after' do
action :remove
package_name 'thor'
ruby new_resource.name
end
test_require('thor', 'thor_after')
# Use bundler to test something that should always be installed.
ruby_gem 'bundler' do
ruby new_resource.name
notifies :create, sentinel_file('bundler'), :immediately
end
# Create and install a Gemfile.
bundle1_path = ::File.join(new_resource.path, 'bundle1')
directory bundle1_path
file ::File.join(bundle1_path, 'Gemfile') do
content <<-EOH
source 'https://rubygems.org/'
gem 'hashie'
gem 'tomlrb', '1.1.0'
EOH
end
bundle1 = bundle_install bundle1_path do
ruby new_resource.name
end
test_require('hashie', bundle: bundle1)
test_require('tomlrb', bundle: bundle1)
test_require('thor', 'thor_bundle', bundle: bundle1)
# Test for bundle exec shebang issues.
bundle2_path = ::File.join(new_resource.path, 'bundle2')
directory bundle2_path
file ::File.join(bundle2_path, 'Gemfile') do
content <<-EOH
source 'https://rubygems.org/'
gem 'unicorn'
EOH
end
file ::File.join(bundle2_path, 'Gemfile.lock') do
content <<-EOH
GEM
remote: https://rubygems.org/
specs:
kgio (2.10.0)
rack (1.6.4)
raindrops (0.15.0)
unicorn (4.9.0)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
PLATFORMS
ruby
DEPENDENCIES
unicorn
BUNDLED WITH
1.10.6
EOH
end
bundle2 = bundle_install bundle2_path do
ruby new_resource.name
deployment true
end
# test_require('unicorn', bundle: bundle2)
ruby_execute "unicorn --version > #{::File.join(new_resource.path, "unicorn_version")}" do
ruby new_resource.name
parent_bundle bundle2
end
end
end
def sentinel_file(name)
file ::File.join(new_resource.path, "sentinel_#{name}") do
action :nothing
end
end
private
def test_version(ruby: new_resource.name)
# Only queue up this resource once, the ivar is just for tracking.
@ruby_version_test ||= file ::File.join(new_resource.path, 'ruby_version.rb') do
user 'root'
group 'root'
mode '644'
content <<-EOH
File.new(ARGV[0], 'w').write(RUBY_VERSION)
EOH
end
ruby_execute "#{@ruby_version_test.path} #{::File.join(new_resource.path, 'version')}" do
ruby ruby if ruby
end
end
def test_require(name, path=name, ruby: new_resource.name, bundle: nil, class_name: nil)
# Only queue up this resource once, the ivar is just for tracking.
@ruby_require_test ||= file ::File.join(new_resource.path, 'require_version.rb') do
user 'root'
group 'root'
mode '644'
content <<-EOH
require 'rubygems'
begin
require "\#{ARGV[0]}/version"
klass = ARGV[1].split('::').inject(Object) {|memo, name| memo.const_get(name) }
File.new(ARGV[2], 'w').write(klass::VERSION)
rescue LoadError
end
EOH
end
class_name ||= Chef::Mixin::ConvertToClassName.convert_to_class_name(name)
ruby_execute "#{@ruby_require_test.path} #{name} #{class_name} #{::File.join(new_resource.path, "require_#{path}")}" do
ruby ruby if ruby
parent_bundle bundle if bundle
end
end
end
end
end
end

View File

@@ -0,0 +1,59 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'poise/utils'
require 'poise_languages'
module PoiseRuby
# Mixin for resources and providers which run Ruby commands.
#
# @since 2.0.0
module RubyCommandMixin
include Poise::Utils::ResourceProviderMixin
module Resource
include PoiseLanguages::Command::Mixin::Resource(:ruby)
# @!attribute gem_binary
# Path to the gem binary.
# @return [String]
attribute(:gem_binary, kind_of: String, default: lazy { default_gem_binary })
private
# Find the default gem binary. If there is a parent use that, otherwise
# use the same logic as {PoiseRuby::RubyProviders::Base#gem_binary}.
#
# @return [String]
def default_gem_binary
if parent_ruby
parent_ruby.gem_binary
else
dir, base = ::File.split(ruby)
# If this ruby is called something weird, bail out.
raise NotImplementedError unless base.start_with?('ruby')
# Allow for names like "ruby2.0" -> "gem2.0".
::File.join(dir, base.sub(/^ruby/, 'gem'))
end
end
end
module Provider
include PoiseLanguages::Command::Mixin::Provider(:ruby)
end
end
end

View File

@@ -0,0 +1,35 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/platform/provider_priority_map'
require 'poise_ruby/ruby_providers/chef'
require 'poise_ruby/ruby_providers/dummy'
require 'poise_ruby/ruby_providers/scl'
require 'poise_ruby/ruby_providers/system'
module PoiseRuby
# Inversion providers for the ruby_runtime resource.
#
# @since 2.0.0
module RubyProviders
Chef::Platform::ProviderPriorityMap.instance.priority(:ruby_runtime, [
PoiseRuby::RubyProviders::Scl,
PoiseRuby::RubyProviders::System,
])
end
end

View File

@@ -0,0 +1,117 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/provider'
require 'poise'
require 'poise_ruby/resources/ruby_gem'
require 'poise_ruby/resources/ruby_runtime'
module PoiseRuby
module RubyProviders
class Base < Chef::Provider
include Poise(inversion: :ruby_runtime)
# Set default inversion options.
#
# @api private
def self.default_inversion_options(node, new_resource)
super.merge({
bundler_version: new_resource.bundler_version,
version: new_resource.version,
})
end
# The `install` action for the `ruby_runtime` resource.
#
# @return [void]
def action_install
notifying_block do
install_ruby
install_bundler
end
end
# The `uninstall` action for the `ruby_runtime` resource.
#
# @return [void]
def action_uninstall
notifying_block do
uninstall_ruby
end
end
# The path to the `ruby` binary.
#
# @abstract
# @return [String]
def ruby_binary
raise NotImplementedError
end
# Output property for environment variables.
#
# @return [Hash<String, String>]
def ruby_environment
# No environment variables needed. Rejoice.
{}
end
# The path to the `gem` binary. Look relative to the
# `ruby` binary for a default implementation.
#
# @return [String]
def gem_binary
dir, base = ::File.split(ruby_binary)
# If this ruby is called something weird, bail out.
raise NotImplementedError unless base.start_with?('ruby')
# Allow for names like "ruby2.0" -> "gem2.0".
::File.join(dir, base.sub(/^ruby/, 'gem'))
end
private
# Install the Ruby runtime. Must be implemented by subclass.
#
# @abstract
# @return [void]
def install_ruby
end
# Uninstall the Ruby runtime. Must be implemented by subclass.
#
# @abstract
# @return [void]
def uninstall_ruby
end
# Install Bundler in to the Ruby runtime.
#
# @return [void]
def install_bundler
# Captured because #options conflicts with Chef::Resource::Package#options.
bundler_version = options[:bundler_version]
return unless bundler_version
ruby_gem 'bundler' do
action :upgrade if bundler_version == true
parent_ruby new_resource
version bundler_version if bundler_version.is_a?(String)
end
end
end
end
end

View File

@@ -0,0 +1,53 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'poise_ruby/error'
require 'poise_ruby/ruby_providers/base'
module PoiseRuby
module RubyProviders
# Inversion provider for the `ruby_runtime` resource to use whatever Ruby is
# currently running, generally Chef's omnibus-d Ruby.
#
# @since 2.0.0
# @provides chef
class ChefRuby < Base
provides(:chef)
# The `install` action for the `ruby_runtime` resource.
#
# @return [void]
def action_install
# No-op, already installed!
end
# The `uninstall` action for the `ruby_runtime` resource.
#
# @return [void]
def action_uninstall
raise PoiseRuby::Error.new("You cannot uninstall Chef's Ruby.")
end
# The path to the running Ruby binary as determined via RbConfig.
#
# @return [String]
def ruby_binary
Gem.ruby
end
end
end
end

View File

@@ -0,0 +1,77 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'poise_ruby/ruby_providers/base'
module PoiseRuby
module RubyProviders
# Inversion provider for the `ruby_runtime` resource to use a fake Ruby,
# for use in unit tests.
#
# @since 2.1.0
# @provides dummy
class Dummy < Base
provides(:dummy)
def self.default_inversion_options(node, resource)
super.merge({
# Manual overrides for dummy data.
ruby_binary: ::File.join('', 'ruby'),
ruby_environment: nil,
gem_binary: nil,
})
end
# The `install` action for the `ruby_runtime` resource.
#
# @return [void]
def action_install
# This space left intentionally blank.
end
# The `uninstall` action for the `ruby_runtime` resource.
#
# @return [void]
def action_uninstall
# This space left intentionally blank.
end
# Path to the non-existent ruby.
#
# @return [String]
def ruby_binary
options['ruby_binary']
end
# Environment for the non-existent Ruby.
#
# @return [String]
def ruby_environment
options['ruby_environment'] || super
end
# Path to the non-existent gem.
#
# @return [String]
def gem_binary
options['gem_binary'] || super
end
end
end
end

View File

@@ -0,0 +1,56 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/resource'
require 'poise_languages'
require 'poise_ruby/ruby_providers/base'
module PoiseRuby
module RubyProviders
class Scl < Base
include PoiseLanguages::Scl::Mixin
provides(:scl)
scl_package('2.4.0', 'rh-ruby24', 'rh-ruby24-ruby-devel')
scl_package('2.3.1', 'rh-ruby23', 'rh-ruby23-ruby-devel')
scl_package('2.2.2', 'rh-ruby22', 'rh-ruby22-ruby-devel')
# On EL7, the system package is Ruby 2.0.0 and is newer than the SCL build.
scl_package('2.0.0', 'ruby200', 'ruby200-ruby-devel', '~> 6.0')
scl_package('1.9.3', 'ruby193', 'ruby193-ruby-devel')
def ruby_binary
::File.join(scl_folder, 'root', 'usr', 'bin', 'ruby')
end
def ruby_environment
scl_environment
end
private
def install_ruby
install_scl_package
end
def uninstall_ruby
uninstall_scl_package
end
end
end
end

View File

@@ -0,0 +1,116 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'chef/resource'
require 'poise_languages'
require 'poise_ruby/error'
require 'poise_ruby/ruby_providers/base'
module PoiseRuby
module RubyProviders
class System < Base
include PoiseLanguages::System::Mixin
provides(:system)
packages('ruby', {
debian: {
'8' => %w{ruby2.1},
'7' => %w{ruby1.9.3 ruby1.9.1 ruby1.8},
# Debian 6 has a ruby1.9.1 package that installs 1.9.2, ignoring it for now.
'6' => %w{ruby1.8},
},
ubuntu: {
'16.04' => %w{ruby2.3},
'14.04' => %w{ruby2.0 ruby1.9.3},
'12.04' => %w{ruby1.9.3 ruby1.8},
'10.04' => %w{ruby1.9.1 ruby1.8},
},
rhel: {default: %w{ruby}},
centos: {default: %w{ruby}},
fedora: {default: %w{ruby}},
# Amazon Linux does actually have packages ruby18, ruby19, ruby20, ruby21.
# Ignoring for now because wooooo non-standard formatting.
amazon: {default: %w{ruby}},
})
def self.default_inversion_options(node, resource)
super.merge({
# Install a separate rubygems package? Only needed for 1.8.
rubygems_package: node['platform_family'] == 'rhel' && node['platform_version'].start_with?('6'),
})
end
# Output value for the Python binary we are installing. Seems to match
# package name on all platforms I've checked.
def ruby_binary
::File.join('', 'usr', 'bin', system_package_name)
end
private
def install_ruby
install_system_packages
install_rubygems_package if options['rubygems_package']
end
def uninstall_ruby
uninstall_system_packages
end
# Ubuntu has no ruby1.9.3-dev package.
def system_dev_package_overrides
super.tap do |overrides|
# WTF Ubuntu, seriously.
overrides['ruby1.9.3'] = 'ruby1.9.1-dev' if node.platform_family?('debian')
end
end
# Install the configured rubygems package.
def install_rubygems_package
package (options['rubygems_package'].is_a?(String) ? options['rubygems_package'] : 'rubygems')
end
def system_package_candidates(version)
[].tap do |names|
# Might as well try it.
names << "ruby#{version}" if version && !['', '1', '2'].include?(version)
# On debian, 1.9.1 and 1.9.3 have special packages.
if match = version.match(/^(\d+\.\d+\.\d+)/)
names << "ruby#{match[1]}"
end
# Normal debian package like ruby2.0.
if match = version.match(/^(\d+\.\d+)/)
names << "ruby#{match[1]}"
end
# Aliases for ruby1 and ruby2
if version == '2' || version == ''
# 2.3 is on there for future proofing. Well, at least giving me a
# buffer zone.
names.concat(%w{ruby2.3 ruby2.2 ruby2.1 ruby2.0})
end
if version == '1' || version == ''
names.concat(%w{ruby1.9.3 ruby1.9 ruby1.8})
end
# For RHEL and friends.
names << 'ruby'
names.uniq!
end
end
end
end
end

View File

@@ -0,0 +1,20 @@
#
# Copyright 2015-2017, Noah Kantrowitz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module PoiseRuby
VERSION = '2.4.0'
end