diff --git a/README.md b/README.md index 7af44ae..9e7dadc 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Cookbooks --------- * build-essential +* utils (https://github.com/davidmarble/cookbook-utils) Attributes ========== @@ -122,8 +123,27 @@ Usage default ------- -Include default recipe in a run list, to get `python`, `pip` and `virtualenv`. Installs python by package or source depending on the platform. +The default recipe installs `python`, `pip`, and `virtualenv`. It also installs +`virtualenvwrapper` if the configuration option `WORKON_HOME` is set (see +the `virtualenvwrapper` recipe below). +The default recipe checks installs the platform version of python and checks if +it meets a minimum version (default is 2.7.1). If the minimum version is not +met by the default python or /usr/local/bin/python, the recipe will install by +source. You can skip the platform python install by specifying `install_method` +as "source" (the default is "package"). + +You can override the default options in your configuration. For example: + + "python": { + "min_version": "2.7.1", + "install_method": "source" + } + +This example would skip the platform package installation, test to see +if python version 2.7.1 or greater is installed already, and if not install +it from source. + package ------- @@ -132,18 +152,44 @@ Installs Python from packages. source ------ -Installs Python from source. +Installs Python from source. pip --- -Installs `pip` from source. +Installs `pip` from source. + +You can set the pip download cache directory and specific system-wide packages +to install via pip in your configuration options. The proper platform +requirements are installed if pip_packages includes "PIL" or "Pillow". + + "python": { + "pip_download_cache": "/tmp/pip", + "pip_packages": ["Pillow==1.7.6"] + } virtualenv ---------- Installs virtualenv using the `python_pip` resource. +virtualenvwrapper +----------------- + +Installs virtualenvwrapper globally using pip. If you include `WORKON_HOME` +in your python configuration, virtualenvwrapper will automatically be +included. You must define `WORKON_HOME_owner` and `WORKON_HOME_group`. +Optionally, you can make `WORKON_HOME` group-writeable so that +virtualenvs can be shared among users. + + "python": { + "WORKON_HOME": "/var/www/envs", + "WORKON_HOME_owner": "www-data", + "WORKON_HOME_group": "www-pub", + "WORKON_HOME_group_writeable": true, + } + + License and Author ================== @@ -151,6 +197,14 @@ Author:: Seth Chisamore () Copyright:: 2011, Opscode, Inc +Edits:: David Marble () + +* 2012: + * min_version check in default recipe + * pip_download_cache and pip_packages, including specialty treatment for + Pillow and PIL + * virtualenvwrapper, including support for shared WORKON_HOME + 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 diff --git a/attributes/default.rb b/attributes/default.rb index 1d0f374..4685b1b 100644 --- a/attributes/default.rb +++ b/attributes/default.rb @@ -18,9 +18,13 @@ # limitations under the License. # +default['python']['min_version'] = '2.7.1' + default['python']['install_method'] = 'package' default['python']['prefix_dir'] = '/usr/local' default['python']['url'] = 'http://www.python.org/ftp/python' -default['python']['version'] = '2.7.1' -default['python']['checksum'] = '80e387bcf57eae8ce26726753584fd63e060ec11682d1145af921e85fd612292' +default['python']['version'] = '2.7.2' +default['python']['checksum'] = '5057eb067eb5b5a6040dbd0e889e06550bde9ec041dadaa855ee9490034cbdab' default['python']['configure_options'] = %W{--prefix=#{python['prefix_dir']}} + +default['python']['WORKON_HOME_group_writeable'] = false diff --git a/metadata.rb b/metadata.rb index f57f356..c6947a6 100644 --- a/metadata.rb +++ b/metadata.rb @@ -5,12 +5,14 @@ version "1.0.6" depends "build-essential" +depends "utils" recipe "python", "Installs python, pip, and virtualenv" recipe "python::package", "Installs python using packages." recipe "python::source", "Installs python from source." recipe "python::pip", "Installs pip from source." recipe "python::virtualenv", "Installs virtualenv using the python_pip resource." +recipe "python::virtualenvwrapper", "Installs virtualenvwrapper." %w{ debian ubuntu centos redhat fedora freebsd }.each do |os| supports os diff --git a/providers/pip.rb b/providers/pip.rb index 0a559de..34e60a6 100644 --- a/providers/pip.rb +++ b/providers/pip.rb @@ -124,9 +124,10 @@ def current_installed_version delimeter = /\s/ version_check_cmd = "pip --version" end + p = shell_out!(version_check_cmd) p.stdout.split(delimeter)[1].strip - rescue Chef::Exceptions::ShellCommandFailed + rescue #Chef::Exceptions::ShellCommandFailed end end diff --git a/providers/virtualenv.rb b/providers/virtualenv.rb index 22f2b7b..c3643bb 100644 --- a/providers/virtualenv.rb +++ b/providers/virtualenv.rb @@ -25,7 +25,12 @@ action :create do unless exists? Chef::Log.info("Creating virtualenv #{@new_resource} at #{@new_resource.path}") - execute "#{virtualenv_cmd} --python=#{@new_resource.interpreter} #{@new_resource.path}" do + if @new_resource.interpreter + python_executable = "--python=#{@new_resource.interpreter} " + else + python_executable = "" + end + execute "#{virtualenv_cmd} #{python_executable}#{@new_resource.path}" do user new_resource.owner if new_resource.owner group new_resource.group if new_resource.group end diff --git a/recipes/default.rb b/recipes/default.rb index 47e2a20..b7d4c9a 100644 --- a/recipes/default.rb +++ b/recipes/default.rb @@ -5,6 +5,10 @@ # # Copyright 2011, Opscode, Inc. # +# Edited:: David Marble +# * 2012 +# * version check to decide if source compilation needed +# # 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 @@ -18,6 +22,81 @@ # limitations under the License. # -include_recipe "python::#{node['python']['install_method']}" +def check_minver(minver) + minver_met = false + python_bindir = "#{node['python']['prefix_dir']}/bin/" + + begin + pythonver = `python -V 2>&1` + rescue + else + currver = pythonver.gsub("Python ","").strip().chomp.strip().split('.').join('').to_i + if currver >= minver + minver_met = true + minver_python = `which python`.chomp + if not minver_python.include?(python_bindir) + # Make sure a link exists to minver_python in python_bindir. + # This is necessary because although /usr/bin/python may meet + # the minimum version required, when a user installs distribute + # and pip, they're placed in /usr/local/bin/. + bash "link minver python" do + cwd python_bindir + code <<-EOF + if [ -s python ]; then + rm python + elif [ -f python ]; then + mv python python.bak + fi + ln -s #{minver_python} python + EOF + end + end + end + end + + if not minver_met + # Check for python already installed from source, because `python` above + # seems to execute with a custom basic PATH different from the PATH set + # for normal interactive logins. You can see this by uncommenting this code: + # env = `env` + # raise "#{currver.to_s}-#{minver.to_s}\n#{env}" + begin + pythonver = `#{python_bindir}python -V 2>&1` + rescue + else + currver = pythonver.gsub("Python ","").strip().chomp.strip().split('.').join('').to_i + if currver >= minver + minver_met = true + end + end + end + return minver_met +end + +minver = node[:python][:min_version].split('.').join('').to_i +minver_met = check_minver(minver) + +if minver_met + # Make sure python-dev installed + if node[:python][:install_method] == "package" + include_recipe "python::package" + end +else + if node[:python][:install_method] == "package" + include_recipe "python::package" + minver_met = check_minver(minver) + end +end + +# Whether source or package, if the minimum version isn't met, +# install via source + +if not minver_met + include_recipe "python::source" +end + include_recipe "python::pip" -include_recipe "python::virtualenv" \ No newline at end of file +include_recipe "python::virtualenv" +if node[:python].attribute?("WORKON_HOME") + include_recipe "python::virtualenvwrapper" +end \ No newline at end of file diff --git a/recipes/pip.rb b/recipes/pip.rb index 22fbd94..021dfed 100644 --- a/recipes/pip.rb +++ b/recipes/pip.rb @@ -32,9 +32,43 @@ bash "install-pip" do cwd Chef::Config[:file_cache_path] + # Problems with non-interactive logins + # http://tickets.opscode.com/browse/CHEF-2288 + # Nothing else seemed able to trigger .bashrc properly. + # See also https://github.com/wijet/chef-sudo/blob/master/lib/chef-sudo.rb code <<-EOF - #{python_bindir}python distribute_setup.py - #{python_bindir}easy_install pip + su -p -l -c '#{python_bindir}python #{Chef::Config[:file_cache_path]}/distribute_setup.py' root + su -p -l -c '#{python_bindir}easy_install pip' root EOF not_if { ::File.exists?(python_bindir+'pip') } end + +if node[:python].attribute?("pip_download_cache") + directory node[:python][:pip_download_cache] do + owner "root" + group "root" + mode "2777" + recursive true + end +end + +if node[:python].attribute?("pip_packages") + node[:python][:pip_packages].each do |pip_pkg| + if pip_pkg.match(/Pillow/) or pip_pkg.match(/PIL/) + pkgs = value_for_platform( + ["debian","ubuntu"] => { + "default" => ["libjpeg62", "libjpeg-dev", "libfreetype6", "libfreetype6-dev", "zlib1g-dev"] + }, + ["centos","redhat","fedora"] => { + "default" => ["libjpeg", "libjpeg-devel", "freetype-devel", "zlib-devel"] + } + ) + pkgs.each do |pkg| + package pkg + end + end + execute "#{python_bindir}pip install #{pip_pkg}" do + cwd Chef::Config[:file_cache_path] + end + end +end diff --git a/recipes/virtualenvwrapper.rb b/recipes/virtualenvwrapper.rb new file mode 100644 index 0000000..c2dacac --- /dev/null +++ b/recipes/virtualenvwrapper.rb @@ -0,0 +1,59 @@ +include_recipe "utils::disable_hg_cert_checking" + +# Ensure WORKON_HOME_owner exists +utils_ensure_user node[:python][:WORKON_HOME_owner] do + update_if_exists false +end + +# Ensure WORKON_HOME_group exists +utils_ensure_group node[:python][:WORKON_HOME_group] do + members [node[:python][:WORKON_HOME_owner]] +end + +# Create WORKON_HOME +directory node[:python][:WORKON_HOME] do + action :create + owner node[:python][:WORKON_HOME_owner] + group node[:python][:WORKON_HOME_group] + mode node[:python][:WORKON_HOME_group_writeable] ? "2775" : "775" + recursive true +end + +# If it should be group-writeable, use ACL +# Change ownership of WORKON_HOME to WORKON_HOME_owner:WORKON_HOME_group +# chmod 2775 on all WORKON_HOME directories +# chmod ug+rw (add user and group read/write) on all WORKON_HOME directories +# Find files executable by owner,group,anyone and make them writable by www-pub +# Find files not executable by anyone and make them rw by www-pub +if node[:python][:WORKON_HOME_group_writeable] + utils_acl node[:python][:WORKON_HOME] + bash "set default ACLs and fix existing perms on #{node[:python][:WORKON_HOME]}" do + code "/usr/local/bin/fixperms #{node[:python][:WORKON_HOME_owner]} #{node[:python][:WORKON_HOME_group]} #{node[:python][:WORKON_HOME]}" + end +end + +python_pip "virtualenvwrapper" do + action :install +end + +# Force an initialization of virtualenvwrapper +script "Initialize virtualenvwrapper" do + interpreter "bash" + user "root" + cwd Chef::Config[:file_cache_path] + code <<-EOH +su -p -l -c 'echo' root + EOH +end + +# Old way on ubuntu +# script "Install davidmarble's virtualenvwrapper" do + # interpreter "bash" + # user "root" + # cwd Chef::Config[:file_cache_path] + # This location is Debian-specific: + # code <<-EOH + # pip install --src=$(python -c \"import os, sys; print os.path.join('/usr/local/lib', 'python' + sys.version[:3], 'dist-packages')\") -e hg+https://bitbucket.org/davidmarble/virtualenvwrapper#egg=virtualenvwrapper + # EOH + # not_if { ::FileTest.exists?("/usr/local/bin/virtualenvwrapper.sh") } +# end diff --git a/resources/virtualenv.rb b/resources/virtualenv.rb index c5840a2..89ca930 100644 --- a/resources/virtualenv.rb +++ b/resources/virtualenv.rb @@ -21,6 +21,6 @@ actions :create, :delete attribute :path, :kind_of => String, :name_attribute => true -attribute :interpreter, :default => 'python2.6' +attribute :interpreter, :default => nil attribute :owner, :regex => Chef::Config[:user_valid_regex] attribute :group, :regex => Chef::Config[:group_valid_regex]