diff --git a/modules/generators/challenges/misc/programming/python_example/manifests/.no_puppet b/modules/generators/challenges/misc/programming/python2_example/manifests/.no_puppet similarity index 100% rename from modules/generators/challenges/misc/programming/python_example/manifests/.no_puppet rename to modules/generators/challenges/misc/programming/python2_example/manifests/.no_puppet diff --git a/modules/generators/challenges/misc/programming/python_example/python_example.pp b/modules/generators/challenges/misc/programming/python2_example/python2_example.pp similarity index 100% rename from modules/generators/challenges/misc/programming/python_example/python_example.pp rename to modules/generators/challenges/misc/programming/python2_example/python2_example.pp diff --git a/modules/generators/challenges/misc/programming/python_example/secgen_local/local.rb b/modules/generators/challenges/misc/programming/python2_example/secgen_local/local.rb similarity index 86% rename from modules/generators/challenges/misc/programming/python_example/secgen_local/local.rb rename to modules/generators/challenges/misc/programming/python2_example/secgen_local/local.rb index c63a7ae0e..4204ed124 100644 --- a/modules/generators/challenges/misc/programming/python_example/secgen_local/local.rb +++ b/modules/generators/challenges/misc/programming/python2_example/secgen_local/local.rb @@ -5,7 +5,7 @@ class RubyExampleScriptGenerator < ScriptChallengeGenerator def initialize super - self.module_name = 'Python Example Script Generator' + self.module_name = 'Python2 Example Script Generator' end diff --git a/modules/generators/challenges/misc/programming/python_example/secgen_metadata.xml b/modules/generators/challenges/misc/programming/python2_example/secgen_metadata.xml similarity index 100% rename from modules/generators/challenges/misc/programming/python_example/secgen_metadata.xml rename to modules/generators/challenges/misc/programming/python2_example/secgen_metadata.xml diff --git a/modules/generators/challenges/misc/programming/python3_example/manifests/.no_puppet b/modules/generators/challenges/misc/programming/python3_example/manifests/.no_puppet new file mode 100644 index 000000000..e69de29bb diff --git a/modules/generators/challenges/misc/programming/python3_example/python3_example.pp b/modules/generators/challenges/misc/programming/python3_example/python3_example.pp new file mode 100644 index 000000000..e69de29bb diff --git a/modules/generators/challenges/misc/programming/python3_example/secgen_local/local.rb b/modules/generators/challenges/misc/programming/python3_example/secgen_local/local.rb new file mode 100644 index 000000000..e4c3e0e27 --- /dev/null +++ b/modules/generators/challenges/misc/programming/python3_example/secgen_local/local.rb @@ -0,0 +1,24 @@ +#!/usr/bin/ruby + +require_relative '../../../../../../../lib/objects/local_script_challenge_generator.rb' +class RubyExampleScriptGenerator < ScriptChallengeGenerator + + def initialize + super + self.module_name = 'Python3 Example Script Generator' + end + + + def interpreter_path + '/usr/bin/python3' + end + + def script_content +"from sys import argv +with open('flag') as f: + print(f.read())" + end + +end + +RubyExampleScriptGenerator.new.run \ No newline at end of file diff --git a/modules/generators/challenges/misc/programming/python3_example/secgen_metadata.xml b/modules/generators/challenges/misc/programming/python3_example/secgen_metadata.xml new file mode 100644 index 000000000..7b143d8fd --- /dev/null +++ b/modules/generators/challenges/misc/programming/python3_example/secgen_metadata.xml @@ -0,0 +1,18 @@ + + + + Ruby Example Challenge Generator + Thomas Shaw + MIT + TODO + + python2_script_challenge + + linux + windows + + script + + \ No newline at end of file diff --git a/modules/utilities/unix/languages/python2/python/Gemfile b/modules/utilities/unix/languages/python2/python/Gemfile new file mode 100644 index 000000000..0d7cc3a61 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/Gemfile @@ -0,0 +1,38 @@ +source ENV['GEM_SOURCE'] || "https://rubygems.org" + +group :system_tests do + gem 'serverspec', :require => false + gem 'beaker', :require => false + gem 'beaker-rspec', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +if facterversion = ENV['FACTER_GEM_VERSION'] + gem 'facter', facterversion, :require => false +else + gem 'facter', :require => false +end + +gem 'rspec-puppet', '~> 2.0', :require => false +gem 'puppet-lint', '~> 2.0', :require => false +gem 'simplecov', :require => false + +gem 'rspec', '~> 2.0', :require => false if RUBY_VERSION >= '1.8.7' && RUBY_VERSION < '1.9' +gem 'rake', '~> 10.0', :require => false if RUBY_VERSION >= '1.8.7' && RUBY_VERSION < '1.9' +gem 'rake', '<= 12.2.1', :require => false if RUBY_VERSION >= '1.9' && RUBY_VERSION < '2.0.0' +gem 'json', '<= 1.8', :require => false if RUBY_VERSION < '2.0.0' +gem 'json_pure', '<= 2.0.1', :require => false if RUBY_VERSION < '2.0.0' +gem 'metadata-json-lint', '0.0.11' if RUBY_VERSION >= '1.8.7' && RUBY_VERSION < '1.9' +gem 'metadata-json-lint', '1.0.0' if RUBY_VERSION >= '1.9' && RUBY_VERSION < '2.0' +gem 'metadata-json-lint' if RUBY_VERSION >= '2.0' + +gem 'puppetlabs_spec_helper', '2.0.2', :require => false if RUBY_VERSION >= '1.8.7' && RUBY_VERSION < '1.9' +gem 'puppetlabs_spec_helper', '>= 2.0.0', :require => false if RUBY_VERSION >= '1.9' +gem 'parallel_tests', '<= 2.9.0', :require => false if RUBY_VERSION < '2.0.0' + +# vim:ft=ruby diff --git a/modules/utilities/unix/languages/python2/python/LICENSE b/modules/utilities/unix/languages/python2/python/LICENSE new file mode 100644 index 000000000..404e08ab9 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright © [2012-2014] [Sergey Stankevich] + + 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. diff --git a/modules/utilities/unix/languages/python2/python/README.md b/modules/utilities/unix/languages/python2/python/README.md new file mode 100644 index 000000000..c3343a1d1 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/README.md @@ -0,0 +1,344 @@ +# puppet-python [![Build Status](https://travis-ci.org/stankevich/puppet-python.svg?branch=master)](https://travis-ci.org/stankevich/puppet-python) + +Puppet module for installing and managing python, pip, virtualenvs and Gunicorn virtual hosts. + +=== + +# Compatibility # + +See `.travis.yml` for compatibility matrix. + +* Puppet v3 (with the future parser) +* Puppet v4 + +## Ruby versions + +* 1.8.7 - Puppet 3 +* 1.9.3 - Puppet 3 +* 2.0.0 - Puppet 3 +* 2.1.9 - Puppet 3 & 4 + +## OS Distributions ## + +This module has been tested to work on the following systems. + +* Debian 6 +* Debian 7 +* Debian 8 +* Debian 9 +* EL 5 +* EL 6 +* EL 7 +* Gentoo (and Sabayon) +* Suse 11 +* Ubuntu 10.04 +* Ubuntu 12.04 +* Ubuntu 14.04 +* Ubuntu 16.04 + +=== + +## Installation + +```shell +git submodule add https://github.com/stankevich/puppet-python.git /path/to/python +``` +OR + +``` shell +puppet module install stankevich-python +``` + +## Usage + +### python + +Installs and manages python, python-pip, python-dev, python-virtualenv and Gunicorn. + +**ensure** - Desired installation state for the Python package. Options are absent, present and latest. Default: present + +**version** - Python version to install. Default: system + +**pip** - Desired installation state for the python-pip package. Options are absent, present and latest. Default: present + +**dev** - Desired installation state for the python-dev package. Options are absent, present and latest. Default: absent + +**virtualenv** - Desired installation state for the virtualenv package. Options are absent, present and latest. Default: absent + +**gunicorn** - Desired installation state for Gunicorn. Options are absent, present and latest. Default: absent + +**manage_gunicorn** - Allow Installation / Removal of Gunicorn. Default: true + +**use_epel** - Boolean to determine if the epel class is used. Default: true on RHEL like systems, false otherwise + +```puppet + class { 'python' : + version => 'system', + pip => 'present', + dev => 'absent', + virtualenv => 'absent', + gunicorn => 'absent', + } +``` + +### python::pip + +Installs and manages packages from pip. + +**pkgname** - the name of the package to install. Required. + +**ensure** - present/latest/absent. You can also specify the version. Default: present + +**virtualenv** - virtualenv to run pip in. Default: system (no virtualenv) + +**url** - URL to install from. Default: none + +**owner** - The owner of the virtualenv to ensure that packages are installed with the correct permissions (must be specified). Default: root + +**proxy** - Proxy server to use for outbound connections. Default: none + +**environment** - Additional environment variables required to install the packages. Default: none + +**egg** - The egg name to use. Default: `$name` of the class, e.g. cx_Oracle + +**install_args** - String of additional flags to pass to pip during installaton. Default: none + +**uninstall_args** - String of additional flags to pass to pip during uninstall. Default: none + +**timeout** - Timeout for the pip install command. Defaults to 1800. +```puppet + python::pip { 'cx_Oracle' : + pkgname => 'cx_Oracle', + ensure => '5.1.2', + virtualenv => '/var/www/project1', + owner => 'appuser', + proxy => 'http://proxy.domain.com:3128', + environment => 'ORACLE_HOME=/usr/lib/oracle/11.2/client64', + install_args => '-e', + timeout => 1800, + } +``` + +### python::requirements + +Installs and manages Python packages from requirements file. + +**virtualenv** - virtualenv to run pip in. Default: system-wide + +**proxy** - Proxy server to use for outbound connections. Default: none + +**owner** - The owner of the virtualenv to ensure that packages are installed with the correct permissions (must be specified). Default: root + +**src** - The `--src` parameter to `pip`, used to specify where to install `--editable` resources; by default no `--src` parameter is passed to `pip`. + +**group** - The group that was used to create the virtualenv. This is used to create the requirements file with correct permissions if it's not present already. + +**manage_requirements** - Create the requirements file if it doesn't exist. Default: true + +```puppet + python::requirements { '/var/www/project1/requirements.txt' : + virtualenv => '/var/www/project1', + proxy => 'http://proxy.domain.com:3128', + owner => 'appuser', + group => 'apps', + } +``` + +### python::virtualenv + +Creates Python virtualenv. + +**ensure** - present/absent. Default: present + +**version** - Python version to use. Default: system default + +**requirements** - Path to pip requirements.txt file. Default: none + +**proxy** - Proxy server to use for outbound connections. Default: none + +**systempkgs** - Copy system site-packages into virtualenv. Default: don't + +**distribute** - Include distribute in the virtualenv. Default: true + +**venv_dir** - The location of the virtualenv if resource path not specified. Must be absolute path. Default: resource name + +**owner** - Specify the owner of this virtualenv + +**group** - Specify the group for this virtualenv + +**index** - Base URL of Python package index. Default: none + +**cwd** - The directory from which to run the "pip install" command. Default: undef + +**timeout** - The maximum time in seconds the "pip install" command should take. Default: 1800 + +```puppet + python::virtualenv { '/var/www/project1' : + ensure => present, + version => 'system', + requirements => '/var/www/project1/requirements.txt', + proxy => 'http://proxy.domain.com:3128', + systempkgs => true, + distribute => false, + venv_dir => '/home/appuser/virtualenvs', + owner => 'appuser', + group => 'apps', + cwd => '/var/www/project1', + timeout => 0, + } +``` + +### python::pyvenv + +Creates Python3 virtualenv. + +**ensure** - present/absent. Default: present + +**version** - Python version to use. Default: system default + +**systempkgs** - Copy system site-packages into virtualenv. Default: don't + +**venv_dir** - The location of the virtualenv if resource path not specified. Must be absolute path. Default: resource name + +**owner** - Specify the owner of this virtualenv + +**group** - Specify the group for this virtualenv + +**path** - Specifies the PATH variable that contains `pyvenv` executable. Default: [ '/bin', '/usr/bin', '/usr/sbin' ] + +**environment** - Specify any environment variables to use when creating pyvenv + +```puppet + python::pyvenv { '/var/www/project1' : + ensure => present, + version => 'system', + systempkgs => true, + venv_dir => '/home/appuser/virtualenvs', + owner => 'appuser', + group => 'apps', + } +``` + +### python::gunicorn + +Manages Gunicorn virtual hosts. + +**ensure** - present/absent. Default: present + +**virtualenv** - Run in virtualenv, specify directory. Default: disabled + +**mode** - Gunicorn mode. wsgi/django. Default: wsgi + +**dir** - Application directory. + +**bind** - Bind on: 'HOST', 'HOST:PORT', 'unix:PATH'. Default: `unix:/tmp/gunicorn-$name.socket` or `unix:${virtualenv}/${name}.socket` + +**environment** - Set ENVIRONMENT variable. Default: none + +**appmodule** - Set the application module name for gunicorn to load when not using Django. Default: `app:app` + +**osenv** - Allows setting environment variables for the gunicorn service. Accepts a hash of 'key': 'value' pairs. Default: false + +**timeout** - Allows setting the gunicorn idle worker process time before being killed. The unit of time is seconds. Default: 30 + +**template** - Which ERB template to use. Default: python/gunicorn.erb + +```puppet + python::gunicorn { 'vhost' : + ensure => present, + virtualenv => '/var/www/project1', + mode => 'wsgi', + dir => '/var/www/project1/current', + bind => 'unix:/tmp/gunicorn.socket', + environment => 'prod', + appmodule => 'app:app', + osenv => { 'DBHOST' => 'dbserver.example.com' }, + timeout => 30, + template => 'python/gunicorn.erb', + } +``` + +### python::dotfile + +Manages arbitrary python dotiles with a simple config hash. + +**ensure** - present/absent. Default: present + +**filename** - Default: $title + +**mode** - Default: 0644 + +**owner** - Default: root + +**group** - Default: root + +**config** Config hash. This will be expanded to an ini-file. Default: {} + +```puppet +python::dotfile { '/var/lib/jenkins/.pip/pip.conf': + ensure => present, + owner => 'jenkins', + group => 'jenkins', + config => { + 'global' => { + 'index-url => 'https://mypypi.acme.com/simple/' + 'extra-index-url => https://pypi.risedev.at/simple/ + } + } +} +``` + +### hiera configuration + +This module supports configuration through hiera. The following example +creates two python3 virtualenvs. The configuration also pip installs a +package into each environment. + +```yaml +python::python_pyvenvs: + "/opt/env1": + version: "system" + "/opt/env2": + version: "system" +python::python_pips: + "nose": + virtualenv: "/opt/env1" + "coverage": + virtualenv: "/opt/env2" +python::python_dotfiles: + "/var/lib/jenkins/.pip/pip.conf": + config: + global: + index-url: "https://mypypi.acme.com/simple/" + extra-index-url: "https://pypi.risedev.at/simple/" +``` + +### Using SCL packages from RedHat or CentOS + +To use this module with Linux distributions in the Red Hat family and python distributions +from softwarecollections.org, set python::provider to 'rhscl' and python::version to the name +of the collection you want to use (e.g., 'python27', 'python33', or 'rh-python34'). + +## Release Notes + +**Version 1.9.8 Notes** +The `pip`, `virtualenv` and `gunicorn` parameters of `Class['python']` have changed. These parameters now accept `absent`, `present` and `latest` rather than `true` and `false`. The boolean values are still supported and are equivalent to `present` and `absent` respectively. Support for these boolean parameters is deprecated and will be removed in a later release. + +**Version 1.7.10 Notes** + +Installation of python-pip previously defaulted to `false` and was not installed. This default is now `true` and python-pip is installed. To prevent the installation of python-pip specify `pip => false` as a parameter when instantiating the `python` puppet class. + +**Version 1.1.x Notes** + +Version `1.1.x` makes several fundamental changes to the core of this module, adding some additional features, improving performance and making operations more robust in general. + +Please note that several changes have been made in `v1.1.x` which make manifests incompatible with the previous version. However, modifying your manifests to suit is trivial. Please see the notes below. + +Currently, the changes you need to make are as follows: + +* All pip definitions MUST include the owner field which specifies which user owns the virtualenv that packages will be installed in. Adding this greatly improves performance and efficiency of this module. +* You must explicitly specify pip => true in the python class if you want pip installed. As such, the pip package is now independent of the dev package and so one can exist without the other. + +## Authors + +[Sergey Stankevich](https://github.com/stankevich) | [Shiva Poudel](https://github.com/shivapoudel) | [Peter Souter](https://github.com/petems) | [Garrett Honeycutt](http://learnpuppet.com) diff --git a/modules/utilities/unix/languages/python2/python/Rakefile b/modules/utilities/unix/languages/python2/python/Rakefile new file mode 100644 index 000000000..abf2783b8 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/Rakefile @@ -0,0 +1,46 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' +require 'puppet-syntax/tasks/puppet-syntax' + +# These two gems aren't always present, for instance +# on Travis with --without development +begin + require 'puppet_blacksmith/rake_tasks' +rescue LoadError +end + +PuppetLint.configuration.fail_on_warnings +PuppetLint.configuration.send('relative') +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.send('disable_140chars') +PuppetLint.configuration.send('disable_class_inherits_from_params_class') +PuppetLint.configuration.send('disable_class_parameter_defaults') +PuppetLint.configuration.send('disable_documentation') +PuppetLint.configuration.send('disable_single_quote_string_with_variables') + +exclude_paths = [ + "pkg/**/*", + "vendor/**/*", + "spec/**/*", +] +PuppetLint.configuration.ignore_paths = exclude_paths +PuppetSyntax.exclude_paths = exclude_paths + +desc 'Validate manifests, templates, ruby files and shell scripts' +task :validate do + Dir['spec/**/*.rb'].each do |ruby_file| + sh "ruby -c #{ruby_file}" unless ruby_file =~ /spec\/fixtures/ + end +end + +desc "Run acceptance tests" +RSpec::Core::RakeTask.new(:acceptance) do |t| + t.pattern = 'spec/acceptance' +end + +desc "Run syntax, lint, and spec tests." +task :test => [ + :syntax, + :lint, + :spec, +] diff --git a/modules/utilities/unix/languages/python2/python/checksums.json b/modules/utilities/unix/languages/python2/python/checksums.json new file mode 100644 index 000000000..585fbaf39 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/checksums.json @@ -0,0 +1,53 @@ +{ + "Gemfile": "c8ff2188da4e5252935d5c3935d480d3", + "LICENSE": "cf2903831f7e18e82a84cce52f44e4a8", + "README.md": "d76d14082c4c2e4a857168218b3adaea", + "Rakefile": "1e5f007de3d45235639cf0ac971250c6", + "lib/facter/pip_version.rb": "94fb2b635dbe71abe0891ba3c1262acf", + "lib/facter/python_release.rb": "f1f7a52cb7981fa67ea07b3d39e088e2", + "lib/facter/python_version.rb": "03f933f329dd8ca388962d5c72048391", + "lib/facter/virtualenv_version.rb": "09837fdf91dfc4daff395874ab63b992", + "manifests/config.pp": "e96cbfc9b920703d3ba91111226de7f5", + "manifests/dotfile.pp": "f6196d257b1c9e8ffde00eef603f4863", + "manifests/gunicorn.pp": "63fe13138fbdde5e14aa43729158f44c", + "manifests/init.pp": "57cfc54ae0af76635df71983e9870234", + "manifests/install.pp": "25f6368563a9aad67fb6e5e43b5dd824", + "manifests/params.pp": "2367376030fad24ba7e4f131d6a87351", + "manifests/pip.pp": "f19814df7ed8151e3aa8f2419e65bd01", + "manifests/pyvenv.pp": "fe680b2c5abc2ac569ff63229f174403", + "manifests/requirements.pp": "866cb2d0ecc5a6415b64b8501c6ca779", + "manifests/virtualenv.pp": "660f1d394d85eff1b4e84ad9210da423", + "metadata.json": "1ecf9220777a1338584728d4994e898f", + "spec/acceptance/class_spec.rb": "784a085709ec2d998961bd6fe45452a6", + "spec/acceptance/facts_test_spec.rb": "c1b5485528b0b1163947e134cea7ce2a", + "spec/acceptance/nodesets/centos-59-x64.yml": "57eb3e471b9042a8ea40978c467f8151", + "spec/acceptance/nodesets/centos-64-x64-pe.yml": "ec075d95760df3d4702abea1ce0a829b", + "spec/acceptance/nodesets/centos-65-x64.yml": "3e5c36e6aa5a690229e720f4048bb8af", + "spec/acceptance/nodesets/debian-70rc1-x64.yml": "715f798e696d3582c66d13f0e8e5f009", + "spec/acceptance/nodesets/debian-73-x64.yml": "b44b7146a828c37482c06879862fb5d9", + "spec/acceptance/nodesets/default.yml": "e27bb4d7526ad3c7a1c4aeb87decec15", + "spec/acceptance/nodesets/ubuntu-server-10044-x64.yml": "75e86400b7889888dc0781c0ae1a1297", + "spec/acceptance/nodesets/ubuntu-server-12042-x64.yml": "d30d73e34cd50b043c7d14e305955269", + "spec/acceptance/nodesets/ubuntu-server-1404-x64.yml": "5f0aed10098ac5b78e4217bb27c7aaf0", + "spec/acceptance/virtualenv_spec.rb": "4494eaf3b0de430fac9df01504c93d2d", + "spec/classes/python_spec.rb": "77af2a49ac7a674661d32633d1245ce7", + "spec/defines/gunicorn_spec.rb": "de12b6b712b04f1175b33cdc6c92b041", + "spec/defines/pip_spec.rb": "c0f0044d49b241370363683331570838", + "spec/defines/pyvenv_spec.rb": "010c4568f19e22603ad712c189192ab8", + "spec/defines/requirements_spec.rb": "9591058f9b823ffc7db27a9d30a10177", + "spec/spec.opts": "a600ded995d948e393fbe2320ba8e51c", + "spec/spec_helper.rb": "74509bb112ac200b2860e116b601fac4", + "spec/spec_helper_acceptance.rb": "59044ad83a006591c63dab149b43afad", + "spec/unit/facter/pip_version_spec.rb": "bc1e73c99891f780d5f6ca60258e8097", + "spec/unit/facter/python_release_spec.rb": "db51f418cc5ad314494a6c3fddc003a6", + "spec/unit/facter/python_version_spec.rb": "be3b595c06e94867efea854b2796d617", + "spec/unit/facter/virtualenv_version_spec.rb": "abb87062002bec85ef1b5b28eeda4101", + "templates/gunicorn.erb": "7d1a6b3340dbdad069e0bdfb14255271", + "templates/inifile.erb": "ce9b0646bd1bcf91a2be0997d44c917f", + "tests/gunicorn.pp": "ac09401c1e329008fef8e9ad6e6971bd", + "tests/init.pp": "251c210b05aab1f84c49a38f7fe48bbe", + "tests/pip.pp": "e3a793d4c714c39df31f3f79f0d7629c", + "tests/pyvenv.pp": "d4c050a2b358d4516d613869fab23113", + "tests/requirements.pp": "4a5e714fbbd623e519f0c07914367730", + "tests/virtualenv.pp": "698f811b9110cbc5d9262f0500bd7ee4" +} \ No newline at end of file diff --git a/modules/utilities/unix/languages/python2/python/lib/facter/pip_version.rb b/modules/utilities/unix/languages/python2/python/lib/facter/pip_version.rb new file mode 100644 index 000000000..71f60aea7 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/lib/facter/pip_version.rb @@ -0,0 +1,9 @@ +# Make pip version available as a fact + +Facter.add("pip_version") do + setcode do + if Facter::Util::Resolution.which('pip') + Facter::Util::Resolution.exec('pip --version 2>&1').match(/^pip (\d+\.\d+\.?\d*).*$/)[1] + end + end +end diff --git a/modules/utilities/unix/languages/python2/python/lib/facter/python_release.rb b/modules/utilities/unix/languages/python2/python/lib/facter/python_release.rb new file mode 100644 index 000000000..348549c18 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/lib/facter/python_release.rb @@ -0,0 +1,33 @@ +# Make python release available as facts + +def get_python_release(executable) + if Facter::Util::Resolution.which(executable) + results = Facter::Util::Resolution.exec("#{executable} -V 2>&1").match(/^.*(\d+\.\d+)\.\d+\+?$/) + if results + results[1] + end + end +end + +Facter.add("python_release") do + setcode do + get_python_release 'python' + end +end + +Facter.add("python2_release") do + setcode do + default_release = get_python_release 'python' + if default_release.nil? or !default_release.start_with?('2') + get_python_release 'python2' + else + default_release + end + end +end + +Facter.add("python3_release") do + setcode do + get_python_release 'python3' + end +end diff --git a/modules/utilities/unix/languages/python2/python/lib/facter/python_version.rb b/modules/utilities/unix/languages/python2/python/lib/facter/python_version.rb new file mode 100644 index 000000000..c4ffa45c7 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/lib/facter/python_version.rb @@ -0,0 +1,33 @@ +# Make python versions available as facts + +def get_python_version(executable) + if Facter::Util::Resolution.which(executable) + results = Facter::Util::Resolution.exec("#{executable} -V 2>&1").match(/^.*(\d+\.\d+\.\d+\+?)$/) + if results + results[1] + end + end +end + +Facter.add("python_version") do + setcode do + get_python_version 'python' + end +end + +Facter.add("python2_version") do + setcode do + default_version = get_python_version 'python' + if default_version.nil? or !default_version.start_with?('2') + get_python_version 'python2' + else + default_version + end + end +end + +Facter.add("python3_version") do + setcode do + get_python_version 'python3' + end +end diff --git a/modules/utilities/unix/languages/python2/python/lib/facter/virtualenv_version.rb b/modules/utilities/unix/languages/python2/python/lib/facter/virtualenv_version.rb new file mode 100644 index 000000000..a0917d09e --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/lib/facter/virtualenv_version.rb @@ -0,0 +1,9 @@ +# Make virtualenv version available as a fact + +Facter.add("virtualenv_version") do + setcode do + if Facter::Util::Resolution.which('virtualenv') + Facter::Util::Resolution.exec('virtualenv --version 2>&1').match(/^(\d+\.\d+\.?\d*).*$/)[0] + end + end +end diff --git a/modules/utilities/unix/languages/python2/python/manifests/config.pp b/modules/utilities/unix/languages/python2/python/manifests/config.pp new file mode 100644 index 000000000..78a424b97 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/config.pp @@ -0,0 +1,40 @@ +# == Define: python::config +# +# Optionally installs the gunicorn service +# +# === Examples +# +# include python::config +# +# === Authors +# +# Sergey Stankevich +# Ashley Penney +# Fotis Gimian +# + +class python::config { + + Class['python::install'] -> Python::Pip <| |> + Class['python::install'] -> Python::Requirements <| |> + Class['python::install'] -> Python::Virtualenv <| |> + + Python::Virtualenv <| |> -> Python::Pip <| |> + + if $python::manage_gunicorn { + if $python::gunicorn != 'absent' { + Class['python::install'] -> Python::Gunicorn <| |> + + Python::Gunicorn <| |> ~> Service['gunicorn'] + + service { 'gunicorn': + ensure => running, + enable => true, + hasrestart => true, + hasstatus => false, + pattern => '/usr/bin/gunicorn', + } + } + } + +} diff --git a/modules/utilities/unix/languages/python2/python/manifests/dotfile.pp b/modules/utilities/unix/languages/python2/python/manifests/dotfile.pp new file mode 100644 index 000000000..5bc70ae98 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/dotfile.pp @@ -0,0 +1,62 @@ +# == Define: python::dotfile +# +# Manages any python dotfiles with a simple config hash. +# +# === Parameters +# +# [*ensure*] +# present|absent. Default: present +# +# [*filename*] +# Filename. Default: $title +# +# [*mode*] +# File mode. Default: 0644 +# +# [*owner*] +# [*group*] +# Owner/group. Default: `root`/`root` +# +# [*config*] +# Config hash. This will be expanded to an ini-file. Default: {} +# +# === Examples +# +# python::dotfile { '/var/lib/jenkins/.pip/pip.conf': +# ensure => present, +# owner => 'jenkins', +# group => 'jenkins', +# config => { +# 'global' => { +# 'index-url => 'https://mypypi.acme.com/simple/' +# 'extra-index-url => https://pypi.risedev.at/simple/ +# } +# } +# } +# +# +define python::dotfile ( + $ensure = 'present', + $filename = $title, + $owner = 'root', + $group = 'root', + $mode = '0644', + $config = {}, +) { + $parent_dir = dirname($filename) + + exec { "create ${title}'s parent dir": + command => "install -o ${owner} -g ${group} -d ${parent_dir}", + path => [ '/usr/bin', '/bin', '/usr/local/bin', ], + creates => $parent_dir, + } + + file { $filename: + ensure => $ensure, + owner => $owner, + group => $group, + mode => $mode, + content => template("${module_name}/inifile.erb"), + require => Exec["create ${title}'s parent dir"], + } +} diff --git a/modules/utilities/unix/languages/python2/python/manifests/gunicorn.pp b/modules/utilities/unix/languages/python2/python/manifests/gunicorn.pp new file mode 100644 index 000000000..bc996e6e0 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/gunicorn.pp @@ -0,0 +1,132 @@ +# == Define: python::gunicorn +# +# Manages Gunicorn virtual hosts. +# +# === Parameters +# +# [*ensure*] +# present|absent. Default: present +# +# [*config_dir*] +# Configure the gunicorn config directory path. Default: /etc/gunicorn.d +# +# [*manage_config_dir*] +# Set if the gunicorn config directory should be created. Default: false +# +# [*virtualenv*] +# Run in virtualenv, specify directory. Default: disabled +# +# [*mode*] +# Gunicorn mode. +# wsgi|django. Default: wsgi +# +# [*dir*] +# Application directory. +# +# [*bind*] +# Bind on: 'HOST', 'HOST:PORT', 'unix:PATH'. +# Default: system-wide: unix:/tmp/gunicorn-$name.socket +# virtualenv: unix:${virtualenv}/${name}.socket +# +# [*environment*] +# Set ENVIRONMENT variable. Default: none +# +# [*appmodule*] +# Set the application module name for gunicorn to load when not using Django. +# Default: app:app +# +# [*osenv*] +# Allows setting environment variables for the gunicorn service. Accepts a +# hash of 'key': 'value' pairs. +# Default: false +# +# [*timeout*] +# Allows setting the gunicorn idle worker process time before being killed. +# The unit of time is seconds. +# Default: 30 +# +# [*template*] +# Which ERB template to use. Default: python/gunicorn.erb +# +# [*args*] +# Custom arguments to add in gunicorn config file. Default: [] +# +# === Examples +# +# python::gunicorn { 'vhost': +# ensure => present, +# virtualenv => '/var/www/project1', +# mode => 'wsgi', +# dir => '/var/www/project1/current', +# bind => 'unix:/tmp/gunicorn.socket', +# environment => 'prod', +# owner => 'www-data', +# group => 'www-data', +# appmodule => 'app:app', +# osenv => { 'DBHOST' => 'dbserver.example.com' }, +# timeout => 30, +# template => 'python/gunicorn.erb', +# } +# +# === Authors +# +# Sergey Stankevich +# Ashley Penney +# Marc Fournier +# +define python::gunicorn ( + $ensure = present, + $config_dir = '/etc/gunicorn.d', + $manage_config_dir = false, + $virtualenv = false, + $mode = 'wsgi', + $dir = false, + $bind = false, + $environment = false, + $owner = 'www-data', + $group = 'www-data', + $appmodule = 'app:app', + $osenv = false, + $timeout = 30, + $workers = false, + $access_log_format = false, + $accesslog = false, + $errorlog = false, + $log_level = 'error', + $template = 'python/gunicorn.erb', + $args = [], +) { + + # Parameter validation + if ! $dir { + fail('python::gunicorn: dir parameter must not be empty') + } + + validate_re($log_level, 'debug|info|warning|error|critical', "Invalid \$log_level value ${log_level}") + + if $manage_config_dir { + file { $config_dir: + ensure => directory, + mode => '0755', + owner => 'root', + group => 'root', + } + file { "${config_dir}/${name}": + ensure => $ensure, + mode => '0644', + owner => 'root', + group => 'root', + content => template($template), + require => File[$config_dir], + } + } else { + file { "${config_dir}/${name}": + ensure => $ensure, + mode => '0644', + owner => 'root', + group => 'root', + content => template($template), + } + } + +} diff --git a/modules/utilities/unix/languages/python2/python/manifests/init.pp b/modules/utilities/unix/languages/python2/python/manifests/init.pp new file mode 100644 index 000000000..83ef9c726 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/init.pp @@ -0,0 +1,150 @@ +# == Class: python +# +# Installs and manages python, python-dev, python-virtualenv and Gunicorn. +# +# === Parameters +# +# [*ensure*] +# Desired installation state for the Python package. Valid options are absent, +# present and latest. Default: present +# +# [*version*] +# Python version to install. Beware that valid values for this differ a) by +# the provider you choose and b) by the osfamily/operatingsystem you are using. +# Default: system default +# Allowed values: +# - provider == pip: everything pip allows as a version after the 'python==' +# - else: 'system', 'pypy', 3/3.3/... +# - Be aware that 'system' usually means python 2.X. +# - 'pypy' actually lets us use pypy as python. +# - 3/3.3/... means you are going to install the python3/python3.3/... +# package, if available on your osfamily. +# +# [*pip*] +# Desired installation state for python-pip. Boolean values are deprecated. +# Default: present +# Allowed values: 'absent', 'present', 'latest' +# +# [*dev*] +# Desired installation state for python-dev. Boolean values are deprecated. +# Default: absent +# Allowed values: 'absent', 'present', 'latest' +# +# [*virtualenv*] +# Desired installation state for python-virtualenv. Boolean values are +# deprecated. Default: absent +# Allowed values: 'absent', 'present', 'latest +# +# [*gunicorn*] +# Desired installation state for Gunicorn. Boolean values are deprecated. +# Default: absent +# Allowed values: 'absent', 'present', 'latest' +# +# [*manage_gunicorn*] +# Allow Installation / Removal of Gunicorn. Default: true +# +# [*provider*] +# What provider to use for installation of the packages, except gunicorn and +# Python itself. Default: system default provider +# Allowed values: 'pip' +# +# [*use_epel*] +# Boolean to determine if the epel class is used. Default: true +# +# === Examples +# +# class { 'python': +# version => 'system', +# pip => 'present', +# dev => 'present', +# virtualenv => 'present', +# gunicorn => 'present', +# } +# +# === Authors +# +# Sergey Stankevich +# Garrett Honeycutt +# +class python ( + $ensure = $python::params::ensure, + $version = $python::params::version, + $pip = $python::params::pip, + $dev = $python::params::dev, + $virtualenv = $python::params::virtualenv, + $gunicorn = $python::params::gunicorn, + $manage_gunicorn = $python::params::manage_gunicorn, + $gunicorn_package_name = $python::params::gunicorn_package_name, + $provider = $python::params::provider, + $valid_versions = $python::params::valid_versions, + $python_pips = { }, + $python_virtualenvs = { }, + $python_pyvenvs = { }, + $python_requirements = { }, + $python_dotfiles = { }, + $use_epel = $python::params::use_epel, + $rhscl_use_public_repository = $python::params::rhscl_use_public_repository, +) inherits python::params{ + + if $provider != undef and $provider != '' { + validate_re($provider, ['^(pip|scl|rhscl)$'], + "Only 'pip', 'rhscl' and 'scl' are valid providers besides the system default. Detected provider is <${provider}>.") + } + + $exec_prefix = $provider ? { + 'scl' => "/usr/bin/scl enable ${version} -- ", + 'rhscl' => "/usr/bin/scl enable ${version} -- ", + default => '', + } + + validate_re($ensure, ['^(absent|present|latest)$']) + validate_re($version, concat(['system', 'pypy'], $valid_versions)) + + if $pip == false or $pip == true { + warning('Use of boolean values for the $pip parameter is deprecated') + } else { + validate_re($pip, ['^(absent|present|latest)$']) + } + + if $virtualenv == false or $virtualenv == true { + warning('Use of boolean values for the $virtualenv parameter is deprecated') + } else { + validate_re($virtualenv, ['^(absent|present|latest)$']) + } + + if $virtualenv == false or $virtualenv == true { + warning('Use of boolean values for the $virtualenv parameter is deprecated') + } else { + validate_re($virtualenv, ['^(absent|present|latest)$']) + } + + if $gunicorn == false or $gunicorn == true { + warning('Use of boolean values for the $gunicorn parameter is deprecated') + } else { + validate_re($gunicorn, ['^(absent|present|latest)$']) + } + + validate_hash($python_dotfiles) + validate_bool($manage_gunicorn) + validate_bool($use_epel) + + # Module compatibility check + $compatible = [ 'Debian', 'RedHat', 'Suse', 'Gentoo' ] + if ! ($::osfamily in $compatible) { + fail("Module is not compatible with ${::operatingsystem}") + } + + # Anchor pattern to contain dependencies + anchor { 'python::begin': } + -> class { 'python::install': } + -> class { 'python::config': } + -> anchor { 'python::end': } + + # Allow hiera configuration of python resources + create_resources('python::pip', $python_pips) + create_resources('python::pyvenv', $python_pyvenvs) + create_resources('python::virtualenv', $python_virtualenvs) + create_resources('python::requirements', $python_requirements) + create_resources('python::dotfile', $python_dotfiles) + +} diff --git a/modules/utilities/unix/languages/python2/python/manifests/install.pp b/modules/utilities/unix/languages/python2/python/manifests/install.pp new file mode 100644 index 000000000..7271f3e66 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/install.pp @@ -0,0 +1,255 @@ +# == Class: python::install +# +# Installs core python packages, +# +# === Examples +# +# include python::install +# +# === Authors +# +# Sergey Stankevich +# Ashley Penney +# Fotis Gimian +# Garrett Honeycutt +# +class python::install { + + $python = $::python::version ? { + 'system' => 'python', + 'pypy' => 'pypy', + default => "${python::version}", # lint:ignore:only_variable_string + } + + $pythondev = $::osfamily ? { + 'RedHat' => "${python}-devel", + 'Debian' => "${python}-dev", + 'Suse' => "${python}-devel", + 'Gentoo' => undef, + } + + $dev_ensure = $python::dev ? { + true => 'present', + false => 'absent', + default => $python::dev, + } + + $pip_ensure = $python::pip ? { + true => 'present', + false => 'absent', + default => $python::pip, + } + + $venv_ensure = $python::virtualenv ? { + true => 'present', + false => 'absent', + default => $python::virtualenv, + } + + package { 'python': + ensure => $python::ensure, + name => $python, + } + + package { 'virtualenv': + ensure => $venv_ensure, + require => Package['python'], + } + + case $python::provider { + pip: { + + package { 'pip': + ensure => $pip_ensure, + require => Package['python'], + } + + if $pythondev { + package { 'python-dev': + ensure => $dev_ensure, + name => $pythondev, + } + } + + # Install pip without pip, see https://pip.pypa.io/en/stable/installing/. + exec { 'bootstrap pip': + command => '/usr/bin/curl https://bootstrap.pypa.io/get-pip.py | python', + unless => 'which pip', + path => [ '/bin', '/usr/bin', '/usr/local/bin' ], + require => Package['python'], + } + + # Puppet is opinionated about the pip command name + file { 'pip-python': + ensure => link, + path => '/usr/bin/pip-python', + target => '/usr/bin/pip', + require => Exec['bootstrap pip'], + } + + Exec['bootstrap pip'] -> File['pip-python'] -> Package <| provider == pip |> + + Package <| title == 'pip' |> { + name => 'pip', + provider => 'pip', + } + Package <| title == 'virtualenv' |> { + name => 'virtualenv', + provider => 'pip', + } + } + scl: { + # SCL is only valid in the RedHat family. If RHEL, package must be + # enabled using the subscription manager outside of puppet. If CentOS, + # the centos-release-SCL will install the repository. + $install_scl_repo_package = $::operatingsystem ? { + 'CentOS' => 'present', + default => 'absent', + } + + package { 'centos-release-scl': + ensure => $install_scl_repo_package, + before => Package['scl-utils'], + } + package { 'scl-utils': + ensure => 'latest', + before => Package['python'], + } + + # This gets installed as a dependency anyway + # package { "${python::version}-python-virtualenv": + # ensure => $venv_ensure, + # require => Package['scl-utils'], + # } + package { "${python}-scldevel": + ensure => $dev_ensure, + require => Package['scl-utils'], + } + if $pip_ensure != 'absent' { + exec { 'python-scl-pip-install': + command => "${python::exec_prefix}easy_install pip", + path => ['/usr/bin', '/bin'], + creates => "/opt/rh/${python::version}/root/usr/bin/pip", + require => Package['scl-utils'], + } + } + } + rhscl: { + # rhscl is RedHat SCLs from softwarecollections.org + if $::python::rhscl_use_public_repository { + $scl_package = "rhscl-${::python::version}-epel-${::operatingsystemmajrelease}-${::architecture}" + package { $scl_package: + source => "https://www.softwarecollections.org/en/scls/rhscl/${::python::version}/epel-${::operatingsystemmajrelease}-${::architecture}/download/${scl_package}.noarch.rpm", + provider => 'rpm', + tag => 'python-scl-repo', + } + } + + Package <| title == 'python' |> { + tag => 'python-scl-package', + } + + Package <| title == 'virtualenv' |> { + name => "${python}-python-virtualenv", + } + + package { "${python}-scldevel": + ensure => $dev_ensure, + tag => 'python-scl-package', + } + + package { "${python}-python-pip": + ensure => $pip_ensure, + tag => 'python-pip-package', + } + + if $::python::rhscl_use_public_repository { + Package <| tag == 'python-scl-repo' |> + -> Package <| tag == 'python-scl-package' |> + } + + Package <| tag == 'python-scl-package' |> + -> Package <| tag == 'python-pip-package' |> + } + default: { + + package { 'pip': + ensure => $pip_ensure, + require => Package['python'], + } + + if $pythondev { + package { 'python-dev': + ensure => $dev_ensure, + name => $pythondev, + alias => $pythondev, + } + } + + if $::osfamily == 'RedHat' { + if $pip_ensure != 'absent' { + if $python::use_epel == true { + include 'epel' + Class['epel'] -> Package['pip'] + } + } + if ($venv_ensure != 'absent') and ($::operatingsystemrelease =~ /^6/) { + if $python::use_epel == true { + include 'epel' + Class['epel'] -> Package['virtualenv'] + } + } + + $virtualenv_package = "${python}-virtualenv" + } else { + if $::lsbdistcodename == 'jessie' { + $virtualenv_package = 'virtualenv' + } elsif $::lsbdistcodename == 'stretch' { + $virtualenv_package = 'virtualenv' + } elsif $::lsbdistcodename == 'xenial' { + $virtualenv_package = 'virtualenv' + } elsif $::osfamily == 'Gentoo' { + $virtualenv_package = 'virtualenv' + } else { + $virtualenv_package = 'python-virtualenv' + } + } + + if "${::python::version}" =~ /^3/ { #lint:ignore:only_variable_string + $pip_category = undef + $pip_package = 'python3-pip' + } elsif ($::osfamily == 'RedHat') and (versioncmp($::operatingsystemmajrelease, '7') >= 0) { + $pip_category = undef + $pip_package = 'python2-pip' + } elsif $::osfamily == 'Gentoo' { + $pip_category = 'dev-python' + $pip_package = 'pip' + } else { + $pip_category = undef + $pip_package = 'python-pip' + } + + Package <| title == 'pip' |> { + name => $pip_package, + category => $pip_category, + } + + Package <| title == 'virtualenv' |> { + name => $virtualenv_package, + } + } + } + + if $python::manage_gunicorn { + $gunicorn_ensure = $python::gunicorn ? { + true => 'present', + false => 'absent', + default => $python::gunicorn, + } + + package { 'gunicorn': + ensure => $gunicorn_ensure, + name => $python::gunicorn_package_name, + } + } +} diff --git a/modules/utilities/unix/languages/python2/python/manifests/params.pp b/modules/utilities/unix/languages/python2/python/manifests/params.pp new file mode 100644 index 000000000..bec6815cd --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/params.pp @@ -0,0 +1,38 @@ +# == Class: python::params +# +# The python Module default configuration settings. +# +class python::params { + $ensure = 'present' + $version = 'system' + $pip = 'present' + $dev = 'absent' + $virtualenv = 'absent' + $gunicorn = 'absent' + $manage_gunicorn = true + $provider = undef + $valid_versions = $::osfamily ? { + 'RedHat' => ['3','27','33'], + 'Debian' => ['3', '3.3', '2.7'], + 'Suse' => [], + 'Gentoo' => ['2.7', '3.3', '3.4', '3.5'] + } + + if $::osfamily == 'RedHat' { + if $::operatingsystem != 'Fedora' { + $use_epel = true + } else { + $use_epel = false + } + } else { + $use_epel = false + } + + $gunicorn_package_name = $::osfamily ? { + 'RedHat' => 'python-gunicorn', + default => 'gunicorn', + } + + $rhscl_use_public_repository = true + +} diff --git a/modules/utilities/unix/languages/python2/python/manifests/pip.pp b/modules/utilities/unix/languages/python2/python/manifests/pip.pp new file mode 100644 index 000000000..1ea2a76a4 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/pip.pp @@ -0,0 +1,275 @@ +# == Define: python::pip +# +# Installs and manages packages from pip. +# +# === Parameters +# +# [*name] +# must be unique +# +# [*pkgname] +# name of the package. If pkgname is not specified, use name (title) instead. +# +# [*ensure*] +# present|absent. Default: present +# +# [*virtualenv*] +# virtualenv to run pip in. +# +# [*url*] +# URL to install from. Default: none +# +# [*owner*] +# The owner of the virtualenv being manipulated. Default: root +# +# [*group*] +# The group of the virtualenv being manipulated. Default: root +# +# [*index*] +# Base URL of Python package index. Default: none (http://pypi.python.org/simple/) +# +# [*proxy*] +# Proxy server to use for outbound connections. Default: none +# +# [*editable*] +# Boolean. If true the package is installed as an editable resource. +# +# [*environment*] +# Additional environment variables required to install the packages. Default: none +# +# [*timeout*] +# The maximum time in seconds the "pip install" command should take. Default: 1800 +# +# [*install_args*] +# String. Any additional installation arguments that will be supplied +# when running pip install. +# +# [*uninstall_args*] +# String. Any additional arguments that will be supplied when running +# pip uninstall. +# +# [*log_dir*] +# String. Log directory. +# +# === Examples +# +# python::pip { 'flask': +# virtualenv => '/var/www/project1', +# proxy => 'http://proxy.domain.com:3128', +# index => 'http://www.example.com/simple/', +# } +# +# === Authors +# +# Sergey Stankevich +# Fotis Gimian +# +define python::pip ( + $pkgname = $name, + $ensure = present, + $virtualenv = 'system', + $url = false, + $owner = 'root', + $group = 'root', + $index = false, + $proxy = false, + $egg = false, + $editable = false, + $environment = [], + $install_args = '', + $uninstall_args = '', + $timeout = 1800, + $log_dir = '/tmp', + $path = ['/usr/local/bin','/usr/bin','/bin', '/usr/sbin'], +) { + + $python_provider = getparam(Class['python'], 'provider') + $python_version = getparam(Class['python'], 'version') + + # Get SCL exec prefix + # NB: this will not work if you are running puppet from scl enabled shell + $exec_prefix = $python_provider ? { + 'scl' => "scl enable ${python_version} -- ", + 'rhscl' => "scl enable ${python_version} -- ", + default => '', + } + + # Parameter validation + if ! $virtualenv { + fail('python::pip: virtualenv parameter must not be empty') + } + + if $virtualenv == 'system' and $owner != 'root' { + fail('python::pip: root user must be used when virtualenv is system') + } + + $cwd = $virtualenv ? { + 'system' => '/', + default => $virtualenv, + } + + validate_absolute_path($cwd) + + $log = $virtualenv ? { + 'system' => $log_dir, + default => $virtualenv, + } + + $pip_env = $virtualenv ? { + 'system' => "${exec_prefix}pip", + default => "${exec_prefix}${virtualenv}/bin/pip", + } + + $pypi_index = $index ? { + false => '', + default => "--index-url=${index}", + } + + $pypi_search_index = $index ? { + false => '', + default => "--index=${index}", + } + + $proxy_flag = $proxy ? { + false => '', + default => "--proxy=${proxy}", + } + + if $editable == true { + $install_editable = ' -e ' + } + else { + $install_editable = '' + } + + #TODO: Do more robust argument checking, but below is a start + if ($ensure == absent) and ($install_args != '') { + fail('python::pip cannot provide install_args with ensure => absent') + } + + if ($ensure == present) and ($uninstall_args != '') { + fail('python::pip cannot provide uninstall_args with ensure => present') + } + + # Check if searching by explicit version. + if $ensure =~ /^((19|20)[0-9][0-9]-(0[1-9]|1[1-2])-([0-2][1-9]|3[0-1])|[0-9]+\.\w+\+?\w*(\.\w+)*)$/ { + $grep_regex = "^${pkgname}==${ensure}\$" + } else { + $grep_regex = $pkgname ? { + /==/ => "^${pkgname}\$", + default => "^${pkgname}==", + } + } + + $egg_name = $egg ? { + false => $pkgname, + default => $egg + } + + $source = $url ? { + false => $pkgname, + /^(\/|[a-zA-Z]\:)/ => $url, + /^(git\+|hg\+|bzr\+|svn\+)(http|https|ssh|svn|sftp|ftp|lp)(:\/\/).+$/ => $url, + default => "${url}#egg=${egg_name}", + } + + # We need to jump through hoops to make sure we issue the correct pip command + # depending on wheel support and versions. + # + # Pip does not support wheels prior to version 1.4.0 + # Pip wheels require setuptools/distribute > 0.8 + # Python 2.6 and older does not support setuptools/distribute > 0.8 + # Pip >= 1.5 tries to use wheels by default, even if wheel package is not + # installed, in this case the --no-use-wheel flag needs to be passed + # Versions prior to 1.5 don't support the --no-use-wheel flag + # + # To check for this we test for wheel parameter using help and then using + # version, this makes sure we only use wheels if they are supported and + # installed + + # Explicit version out of VCS when PIP supported URL is provided + if $source =~ /^(git\+|hg\+|bzr\+|svn\+)(http|https|ssh|svn|sftp|ftp|lp)(:\/\/).+$/ { + if $ensure != present and $ensure != latest { + exec { "pip_install_${name}": + command => "${pip_env} wheel --help > /dev/null 2>&1 && { ${pip_env} wheel --version > /dev/null 2>&1 || wheel_support_flag='--no-use-wheel'; } ; { ${pip_env} --log ${log}/pip.log install ${install_args} \$wheel_support_flag ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source}@${ensure}#egg=${egg_name} || ${pip_env} --log ${log}/pip.log install ${install_args} ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source}@${ensure}#egg=${egg_name} ;}", + unless => "${pip_env} freeze | grep -i -e ${grep_regex}", + user => $owner, + group => $group, + cwd => $cwd, + environment => $environment, + timeout => $timeout, + path => $path, + } + } else { + exec { "pip_install_${name}": + command => "${pip_env} wheel --help > /dev/null 2>&1 && { ${pip_env} wheel --version > /dev/null 2>&1 || wheel_support_flag='--no-use-wheel'; } ; { ${pip_env} --log ${log}/pip.log install ${install_args} \$wheel_support_flag ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source} || ${pip_env} --log ${log}/pip.log install ${install_args} ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source} ;}", + unless => "${pip_env} freeze | grep -i -e ${grep_regex}", + user => $owner, + group => $group, + cwd => $cwd, + environment => $environment, + timeout => $timeout, + path => $path, + } + } + } else { + case $ensure { + /^((19|20)[0-9][0-9]-(0[1-9]|1[1-2])-([0-2][1-9]|3[0-1])|[0-9]+\.\w+\+?\w*(\.\w+)*)$/: { + # Version formats as per http://guide.python-distribute.org/specification.html#standard-versioning-schemes + # Explicit version. + exec { "pip_install_${name}": + command => "${pip_env} wheel --help > /dev/null 2>&1 && { ${pip_env} wheel --version > /dev/null 2>&1 || wheel_support_flag='--no-use-wheel'; } ; { ${pip_env} --log ${log}/pip.log install ${install_args} \$wheel_support_flag ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source}==${ensure} || ${pip_env} --log ${log}/pip.log install ${install_args} ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source}==${ensure} ;}", + unless => "${pip_env} freeze | grep -i -e ${grep_regex} || ${pip_env} list | sed -e 's/[ ]\\+/==/' -e 's/[()]//g' | grep -i -e ${grep_regex}", + user => $owner, + group => $group, + cwd => $cwd, + environment => $environment, + timeout => $timeout, + path => $path, + } + } +# + present: { + # Whatever version is available. + exec { "pip_install_${name}": + command => "${pip_env} wheel --help > /dev/null 2>&1 && { ${pip_env} wheel --version > /dev/null 2>&1 || wheel_support_flag='--no-use-wheel'; } ; { ${pip_env} --log ${log}/pip.log install \$wheel_support_flag ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source} || ${pip_env} --log ${log}/pip.log install ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source} ;}", + unless => "${pip_env} freeze | grep -i -e ${grep_regex} || ${pip_env} list | sed -e 's/[ ]\\+/==/' -e 's/[()]//g' | grep -i -e ${grep_regex}", + user => $owner, + group => $group, + cwd => $cwd, + environment => $environment, + timeout => $timeout, + path => $path, + } + } + + latest: { + # Latest version. + exec { "pip_install_${name}": + command => "${pip_env} wheel --help > /dev/null 2>&1 && { ${pip_env} wheel --version > /dev/null 2>&1 || wheel_support_flag='--no-use-wheel'; } ; { ${pip_env} --log ${log}/pip.log install --upgrade \$wheel_support_flag ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source} || ${pip_env} --log ${log}/pip.log install --upgrade ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source} ;}", + unless => "${pip_env} search ${pypi_search_index} ${proxy_flag} ${source} | grep -i INSTALLED.*latest", + user => $owner, + group => $group, + cwd => $cwd, + environment => $environment, + timeout => $timeout, + path => $path, + } + } + + default: { + # Anti-action, uninstall. + exec { "pip_uninstall_${name}": + command => "echo y | ${pip_env} uninstall ${uninstall_args} ${proxy_flag} ${name}", + onlyif => "${pip_env} freeze | grep -i -e ${grep_regex}", + user => $owner, + group => $group, + cwd => $cwd, + environment => $environment, + timeout => $timeout, + path => $path, + } + } + } + } +} diff --git a/modules/utilities/unix/languages/python2/python/manifests/pyvenv.pp b/modules/utilities/unix/languages/python2/python/manifests/pyvenv.pp new file mode 100644 index 000000000..5f8feceaf --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/pyvenv.pp @@ -0,0 +1,104 @@ +# == Define: python::pyvenv +# +# Create a Python3 virtualenv using pyvenv. +# +# === Parameters +# +# [*ensure*] +# present|absent. Default: present +# +# [*version*] +# Python version to use. Default: system default +# +# [*systempkgs*] +# Copy system site-packages into virtualenv. Default: don't +# +# [*venv_dir*] +# Directory to install virtualenv to. Default: $name +# +# [*owner*] +# The owner of the virtualenv being manipulated. Default: root +# +# [*group*] +# The group relating to the virtualenv being manipulated. Default: root +# +# [*mode*] +# Optionally specify directory mode. Default: 0755 +# +# [*path*] +# Specifies the PATH variable. Default: [ '/bin', '/usr/bin', '/usr/sbin' ] + +# [*environment*] +# Optionally specify environment variables for pyvenv +# +# === Examples +# +# python::venv { '/var/www/project1': +# ensure => present, +# version => 'system', +# systempkgs => true, +# } +# +# === Authors +# +# Sergey Stankevich +# Ashley Penney +# Marc Fournier +# Fotis Gimian +# Seth Cleveland +# +define python::pyvenv ( + $ensure = present, + $version = 'system', + $systempkgs = false, + $venv_dir = $name, + $owner = 'root', + $group = 'root', + $mode = '0755', + $path = [ '/bin', '/usr/bin', '/usr/sbin', '/usr/local/bin' ], + $environment = [], +) { + + include ::python + + if $ensure == 'present' { + + $virtualenv_cmd = $version ? { + 'system' => "${python::exec_prefix}pyvenv", + default => "${python::exec_prefix}pyvenv-${version}", + } + + if ( $systempkgs == true ) { + $system_pkgs_flag = '--system-site-packages' + } else { + $system_pkgs_flag = '' + } + + file { $venv_dir: + ensure => directory, + owner => $owner, + group => $group, + mode => $mode + } + + exec { "python_virtualenv_${venv_dir}": + command => "${virtualenv_cmd} --clear ${system_pkgs_flag} ${venv_dir}", + user => $owner, + creates => "${venv_dir}/bin/activate", + path => $path, + cwd => '/tmp', + environment => $environment, + unless => "grep '^[\\t ]*VIRTUAL_ENV=[\\\\'\\\"]*${venv_dir}[\\\"\\\\'][\\t ]*$' ${venv_dir}/bin/activate", #Unless activate exists and VIRTUAL_ENV is correct we re-create the virtualenv + require => File[$venv_dir], + } + } elsif $ensure == 'absent' { + file { $venv_dir: + ensure => absent, + force => true, + recurse => true, + purge => true, + } + } else { + fail( "Illegal ensure value: ${ensure}. Expected (present or absent)") + } +} diff --git a/modules/utilities/unix/languages/python2/python/manifests/requirements.pp b/modules/utilities/unix/languages/python2/python/manifests/requirements.pp new file mode 100644 index 000000000..75d2b698b --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/requirements.pp @@ -0,0 +1,141 @@ +# == Define: python::requirements +# +# Installs and manages Python packages from requirements file. +# +# === Parameters +# +# [*requirements*] +# Path to the requirements file. Defaults to the resource name +# +# [*virtualenv*] +# virtualenv to run pip in. Default: system-wide +# +# [*owner*] +# The owner of the virtualenv being manipulated. Default: root +# +# [*group*] +# The group relating to the virtualenv being manipulated. Default: root +# +# [*proxy*] +# Proxy server to use for outbound connections. Default: none +# +# [*src*] +# Pip --src parameter; if the requirements file contains --editable resources, +# this parameter specifies where they will be installed. See the pip +# documentation for more. Default: none (i.e. use the pip default). +# +# [*environment*] +# Additional environment variables required to install the packages. Default: none +# +# [*forceupdate*] +# Run a pip install requirements even if we don't receive an event from the +# requirements file - Useful for when the requirements file is written as part of a +# resource other than file (E.g vcsrepo) +# +# [*cwd*] +# The directory from which to run the "pip install" command. Default: undef +# +# [*extra_pip_args*] +# Extra arguments to pass to pip after the requirements file +# +# [*manage_requirements*] +# Create the requirements file if it doesn't exist. Default: true +# +# [*fix_requirements_owner*] +# Change owner and group of requirements file. Default: true +# +# [*log_dir*] +# String. Log directory. +# +# [*timeout*] +# The maximum time in seconds the "pip install" command should take. Default: 1800 +# +# === Examples +# +# python::requirements { '/var/www/project1/requirements.txt': +# virtualenv => '/var/www/project1', +# proxy => 'http://proxy.domain.com:3128', +# } +# +# === Authors +# +# Sergey Stankevich +# Ashley Penney +# Fotis Gimian +# +define python::requirements ( + $requirements = $name, + $virtualenv = 'system', + $owner = 'root', + $group = 'root', + $proxy = false, + $src = false, + $environment = [], + $forceupdate = false, + $cwd = undef, + $extra_pip_args = '', + $manage_requirements = true, + $fix_requirements_owner = true, + $log_dir = '/tmp', + $timeout = 1800, +) { + + include ::python + + if $virtualenv == 'system' and ($owner != 'root' or $group != 'root') { + fail('python::pip: root user must be used when virtualenv is system') + } + + if $fix_requirements_owner { + $owner_real = $owner + $group_real = $group + } else { + $owner_real = undef + $group_real = undef + } + + $log = $virtualenv ? { + 'system' => $log_dir, + default => $virtualenv, + } + + $pip_env = $virtualenv ? { + 'system' => "${::python::exec_prefix} pip", + default => "${::python::exec_prefix} ${virtualenv}/bin/pip", + } + + $proxy_flag = $proxy ? { + false => '', + default => "--proxy=${proxy}", + } + + $src_flag = $src ? { + false => '', + default => "--src=${src}", + } + + # This will ensure multiple python::virtualenv definitions can share the + # the same requirements file. + if !defined(File[$requirements]) and $manage_requirements == true { + file { $requirements: + ensure => present, + mode => '0644', + owner => $owner_real, + group => $group_real, + audit => content, + replace => false, + content => '# Puppet will install and/or update pip packages listed here', + } + } + + exec { "python_requirements${name}": + provider => shell, + command => "${pip_env} --log ${log}/pip.log install ${proxy_flag} ${src_flag} -r ${requirements} ${extra_pip_args}", + refreshonly => !$forceupdate, + timeout => $timeout, + cwd => $cwd, + user => $owner, + subscribe => File[$requirements], + environment => $environment, + } +} diff --git a/modules/utilities/unix/languages/python2/python/manifests/virtualenv.pp b/modules/utilities/unix/languages/python2/python/manifests/virtualenv.pp new file mode 100644 index 000000000..a5d9ae4b6 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/manifests/virtualenv.pp @@ -0,0 +1,208 @@ +# == Define: python::virtualenv +# +# Creates Python virtualenv. +# +# === Parameters +# +# [*ensure*] +# present|absent. Default: present +# +# [*version*] +# Python version to use. Default: system default +# +# [*requirements*] +# Path to pip requirements.txt file. Default: none +# +# [*systempkgs*] +# Copy system site-packages into virtualenv. Default: don't +# If virtualenv version < 1.7 this flag has no effect since +# [*venv_dir*] +# Directory to install virtualenv to. Default: $name +# +# [*distribute*] +# Include distribute in the virtualenv. Default: true +# +# [*index*] +# Base URL of Python package index. Default: none (http://pypi.python.org/simple/) +# +# [*owner*] +# The owner of the virtualenv being manipulated. Default: root +# +# [*group*] +# The group relating to the virtualenv being manipulated. Default: root +# +# [*mode*] +# Optionally specify directory mode. Default: 0755 +# +# [*proxy*] +# Proxy server to use for outbound connections. Default: none +# +# [*environment*] +# Additional environment variables required to install the packages. Default: none +# +# [*path*] +# Specifies the PATH variable. Default: [ '/bin', '/usr/bin', '/usr/sbin' ] +# +# [*cwd*] +# The directory from which to run the "pip install" command. Default: undef +# +# [*timeout*] +# The maximum time in seconds the "pip install" command should take. Default: 1800 +# +# [*pip_args*] +# Arguments to pass to pip during initialization. Default: blank +# +# [*extra_pip_args*] +# Extra arguments to pass to pip after requirements file. Default: blank +# +# === Examples +# +# python::virtualenv { '/var/www/project1': +# ensure => present, +# version => 'system', +# requirements => '/var/www/project1/requirements.txt', +# proxy => 'http://proxy.domain.com:3128', +# systempkgs => true, +# index => 'http://www.example.com/simple/', +# } +# +# === Authors +# +# Sergey Stankevich +# Shiva Poudel +# +define python::virtualenv ( + $ensure = present, + $version = 'system', + $requirements = false, + $systempkgs = false, + $venv_dir = $name, + $distribute = true, + $index = false, + $owner = 'root', + $group = 'root', + $mode = '0755', + $proxy = false, + $environment = [], + $path = [ '/bin', '/usr/bin', '/usr/sbin', '/usr/local/bin' ], + $cwd = undef, + $timeout = 1800, + $pip_args = '', + $extra_pip_args = '', + $virtualenv = undef +) { + include ::python + + if $ensure == 'present' { + $python = $version ? { + 'system' => 'python', + 'pypy' => 'pypy', + default => "python${version}", + } + + if $virtualenv == undef { + $used_virtualenv = 'virtualenv' + } else { + $used_virtualenv = $virtualenv + } + + $proxy_flag = $proxy ? { + false => '', + default => "--proxy=${proxy}", + } + + $proxy_command = $proxy ? { + false => '', + default => "&& export http_proxy=${proxy}", + } + + # Virtualenv versions prior to 1.7 do not support the + # --system-site-packages flag, default off for prior versions + # Prior to version 1.7 the default was equal to --system-site-packages + # and the flag --no-site-packages had to be passed to do the opposite + $_virtualenv_version = getvar('virtualenv_version') ? { + /.*/ => getvar('virtualenv_version'), + default => '', + } + if (( versioncmp($_virtualenv_version,'1.7') > 0 ) and ( $systempkgs == true )) { + $system_pkgs_flag = '--system-site-packages' + } elsif (( versioncmp($_virtualenv_version,'1.7') < 0 ) and ( $systempkgs == false )) { + $system_pkgs_flag = '--no-site-packages' + } else { + $system_pkgs_flag = $systempkgs ? { + true => '--system-site-packages', + false => '--no-site-packages', + default => fail('Invalid value for systempkgs. Boolean value is expected') + } + } + + $distribute_pkg = $distribute ? { + true => 'distribute', + default => 'setuptools', + } + $pypi_index = $index ? { + false => '', + default => "-i ${index}", + } + + # Python 2.6 and older does not support setuptools/distribute > 0.8 which + # is required for pip wheel support, pip therefor requires --no-use-wheel flag + # if the # pip version is more recent than 1.4.1 but using an old python or + # setuputils/distribute version + # To check for this we test for wheel parameter using help and then using + # version, this makes sure we only use wheels if they are supported + + file { $venv_dir: + ensure => directory, + owner => $owner, + group => $group, + mode => $mode + } + + $virtualenv_cmd = "${python::exec_prefix}${used_virtualenv}" + + $pip_cmd = "${python::exec_prefix}${venv_dir}/bin/pip" + $pip_flags = "${pypi_index} ${proxy_flag} ${pip_args}" + + exec { "python_virtualenv_${venv_dir}": + command => "true ${proxy_command} && ${virtualenv_cmd} ${system_pkgs_flag} -p ${python} ${venv_dir} && ${pip_cmd} --log ${venv_dir}/pip.log install ${pip_flags} --upgrade pip && ${pip_cmd} install ${pip_flags} --upgrade ${distribute_pkg}", + user => $owner, + creates => "${venv_dir}/bin/activate", + path => $path, + cwd => '/tmp', + environment => $environment, + unless => "grep '^[\\t ]*VIRTUAL_ENV=[\\\\'\\\"]*${venv_dir}[\\\"\\\\'][\\t ]*$' ${venv_dir}/bin/activate", #Unless activate exists and VIRTUAL_ENV is correct we re-create the virtualenv + require => File[$venv_dir], + } + + if $requirements { + exec { "python_requirements_initial_install_${requirements}_${venv_dir}": + command => "${pip_cmd} --log ${venv_dir}/pip.log install ${pypi_index} ${proxy_flag} --no-binary :all: -r ${requirements} ${extra_pip_args}", + refreshonly => true, + timeout => $timeout, + user => $owner, + subscribe => Exec["python_virtualenv_${venv_dir}"], + environment => $environment, + cwd => $cwd + } + + python::requirements { "${requirements}_${venv_dir}": + requirements => $requirements, + virtualenv => $venv_dir, + proxy => $proxy, + owner => $owner, + group => $group, + cwd => $cwd, + require => Exec["python_virtualenv_${venv_dir}"], + extra_pip_args => $extra_pip_args, + } + } + } elsif $ensure == 'absent' { + file { $venv_dir: + ensure => absent, + force => true, + recurse => true, + purge => true, + } + } +} diff --git a/modules/utilities/unix/languages/python2/python/metadata.json b/modules/utilities/unix/languages/python2/python/metadata.json new file mode 100644 index 000000000..da85a2a65 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/metadata.json @@ -0,0 +1,71 @@ +{ + "name": "stankevich-python", + "version": "1.19.0", + "author": "stankevich", + "summary": "Python Module", + "license": "Apache-2.0", + "source": "git://github.com/stankevich/puppet-python.git", + "project_page": "https://github.com/stankevich/puppet-python", + "issues_url": "https://github.com/stankevich/puppet-python/issues", + "dependencies": [ + { + "name": "puppetlabs/stdlib", + "version_requirement": ">= 4.6.0 < 6.0.0" + }, + { + "name": "stahnma/epel", + "version_requirement": ">= 1.2.2 < 2.0.0" + } + ], + "data_provider": null, + "description": "Puppet module for Python", + "tags": [ + "python", + "pip", + "virtualenv", + "gunicorn" + ], + "operatingsystem_support": [ + { + "operatingsystem": "CentOS", + "operatingsystemrelease": [ + "5", + "6", + "7" + ] + }, + { + "operatingsystem": "Debian", + "operatingsystemrelease": [ + "6", + "7", + "8", + "9" + ] + }, + { + "operatingsystem": "Gentoo" + }, + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": [ + "10.04", + "12.04", + "14.04", + "16.04" + ] + }, + { + "operatingsystem": "SLES", + "operatingsystemrelease": [ + "11.3" + ] + } + ], + "requirements": [ + { + "name": "puppet", + "version_requirement": ">=3.0.0 < 5.0.0" + } + ] +} diff --git a/modules/utilities/unix/languages/python2/python/python.pp b/modules/utilities/unix/languages/python2/python/python.pp new file mode 100644 index 000000000..de7ed6a0e --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/python.pp @@ -0,0 +1,7 @@ +class { 'python': + version => 'system', + pip => 'present', + dev => 'absent', + virtualenv => 'absent', + gunicorn => 'absent', +} \ No newline at end of file diff --git a/modules/utilities/unix/languages/python2/python/secgen_metadata.xml b/modules/utilities/unix/languages/python2/python/secgen_metadata.xml new file mode 100644 index 000000000..1e4b4372b --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/secgen_metadata.xml @@ -0,0 +1,25 @@ + + + + Python 2 programming language + Thomas Shaw + stankevich + Apache v2 + An installation of Python + + language + python + linux + + + https://forge.puppet.com/stankevich/python + + ruby + Apache v2 + + + update + + \ No newline at end of file diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/class_spec.rb b/modules/utilities/unix/languages/python2/python/spec/acceptance/class_spec.rb new file mode 100644 index 000000000..0b65659cf --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/class_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper_acceptance' + +describe 'python class' do + + context 'default parameters' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'python': } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/facts_test_spec.rb b/modules/utilities/unix/languages/python2/python/spec/acceptance/facts_test_spec.rb new file mode 100644 index 000000000..6393c01f4 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/facts_test_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper_acceptance' + +describe 'python class' do + + context 'facts' do + install_python = <<-EOS + class { 'python' : + version => 'system', + pip => 'present', + virtualenv => 'present', + } + EOS + + fact_notices = <<-EOS + notify{"pip_version: ${::pip_version}":} + notify{"system_python_version: ${::system_python_version}":} + notify{"python_version: ${::python_version}":} + notify{"virtualenv_version: ${::virtualenv_version}":} + EOS + + it 'should output python facts when not installed' do + apply_manifest(fact_notices, :catch_failures => true) do |r| + expect(r.stdout).to match(/python_version: \S+/) + expect(r.stdout).to match(/pip_version: \S+/) + expect(r.stdout).to match(/virtualenv_version: \S+/) + expect(r.stdout).to match(/system_python_version: \S+/) + end + end + + it 'sets up python' do + apply_manifest(install_python, :catch_failures => true) + end + + it 'should output python facts when installed' do + apply_manifest(fact_notices, :catch_failures => true) do |r| + expect(r.stdout).to match(/python_version: \S+/) + expect(r.stdout).to match(/pip_version: \S+/) + expect(r.stdout).to match(/virtualenv_version: \S+/) + expect(r.stdout).to match(/system_python_version: \S+/) + end + end + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/centos-59-x64.yml b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/centos-59-x64.yml new file mode 100644 index 000000000..2ad90b86a --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/centos-59-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-59-x64: + roles: + - master + platform: el-5-x86_64 + box : centos-59-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: git diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/centos-64-x64-pe.yml b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/centos-64-x64-pe.yml new file mode 100644 index 000000000..7d9242f1b --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/centos-64-x64-pe.yml @@ -0,0 +1,12 @@ +HOSTS: + centos-64-x64: + roles: + - master + - database + - dashboard + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: pe diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/centos-65-x64.yml b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/centos-65-x64.yml new file mode 100644 index 000000000..4e2cb809e --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/centos-65-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-65-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-65-x64-vbox436-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-65-x64-virtualbox-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/debian-70rc1-x64.yml b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/debian-70rc1-x64.yml new file mode 100644 index 000000000..4b55677f4 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/debian-70rc1-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + debian-70rc1-x64: + roles: + - master + platform: debian-70rc1-x64 + box : debian-70rc1-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/debian-73-x64.yml b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/debian-73-x64.yml new file mode 100644 index 000000000..d71434551 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/debian-73-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + debian-73-x64: + roles: + - master + platform: debian-7-amd64 + box : debian-73-x64-virtualbox-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-73-x64-virtualbox-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git \ No newline at end of file diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/default.yml b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/default.yml new file mode 100644 index 000000000..45af98934 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/default.yml @@ -0,0 +1,11 @@ +HOSTS: + ubuntu-server-12042-x64: + roles: + - master + platform: ubuntu-server-12.04-amd64 + box : ubuntu-server-12042-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss + vagrant_ssh_port_random: true diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml new file mode 100644 index 000000000..5ca1514e4 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + ubuntu-server-10044-x64: + roles: + - master + platform: ubuntu-10.04-amd64 + box : ubuntu-server-10044-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml new file mode 100644 index 000000000..d065b304f --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + ubuntu-server-12042-x64: + roles: + - master + platform: ubuntu-12.04-amd64 + box : ubuntu-server-12042-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml new file mode 100644 index 000000000..cba1cd04c --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + ubuntu-server-1404-x64: + roles: + - master + platform: ubuntu-14.04-amd64 + box : puppetlabs/ubuntu-14.04-64-nocm + box_url : https://vagrantcloud.com/puppetlabs/ubuntu-14.04-64-nocm + hypervisor : vagrant +CONFIG: + log_level : debug + type: git diff --git a/modules/utilities/unix/languages/python2/python/spec/acceptance/virtualenv_spec.rb b/modules/utilities/unix/languages/python2/python/spec/acceptance/virtualenv_spec.rb new file mode 100644 index 000000000..2dfbdc3dd --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/acceptance/virtualenv_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper_acceptance' + +describe 'python class' do + + context 'default parameters' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'python' : + version => 'system', + pip => 'present', + virtualenv => 'present', + } + -> + python::virtualenv { 'venv' : + ensure => 'present', + systempkgs => false, + venv_dir => '/opt/venv', + owner => 'root', + group => 'root', + } + -> + python::pip { 'rpyc' : + ensure => '3.2.3', + virtualenv => '/opt/venv', + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/classes/python_spec.rb b/modules/utilities/unix/languages/python2/python/spec/classes/python_spec.rb new file mode 100644 index 000000000..1ec0de42b --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/classes/python_spec.rb @@ -0,0 +1,423 @@ +require 'spec_helper' + +describe 'python', :type => :class do + context "on Debian OS" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :lsbdistcodename => 'squeeze', + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '6', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :concat_basedir => '/dne', + } + end + + it { is_expected.to contain_class("python::install") } + # Base debian packages. + it { is_expected.to contain_package("python") } + it { is_expected.to contain_package("python-dev") } + it { is_expected.to contain_package("pip") } + # Basic python packages (from pip) + it { is_expected.to contain_package("virtualenv")} + + describe "with python::dev" do + context "true" do + let (:params) {{ :dev => 'present' }} + it { is_expected.to contain_package("python-dev").with_ensure('present') } + end + context "empty/default" do + it { is_expected.to contain_package("python-dev").with_ensure('absent') } + end + end + + describe "with manage_gunicorn" do + context "true" do + let (:params) {{ :manage_gunicorn => true }} + it { is_expected.to contain_package("gunicorn") } + end + context "empty args" do + #let (:params) {{ :manage_gunicorn => '' }} + it { is_expected.to contain_package("gunicorn") } + end + context "false" do + let (:params) {{ :manage_gunicorn => false }} + it {is_expected.not_to contain_package("gunicorn")} + end + end + + describe "with python::provider" do + context "pip" do + let (:params) {{ :provider => 'pip' }} + it { is_expected.to contain_package("virtualenv").with( + 'provider' => 'pip' + )} + it { is_expected.to contain_package("pip").with( + 'provider' => 'pip' + )} + end + + # python::provider + context "default" do + let (:params) {{ :provider => '' }} + it { is_expected.to contain_package("virtualenv")} + it { is_expected.to contain_package("pip")} + + describe "with python::virtualenv" do + context "true" do + let (:params) {{ :provider => '', :virtualenv => true }} + it { is_expected.to contain_package("virtualenv").with_ensure('present') } + end + end + + describe "without python::virtualenv" do + context "default/empty" do + let (:params) {{ :provider => '' }} + it { is_expected.to contain_package("virtualenv").with_ensure('absent') } + end + end + end + end + + describe "with python::dev" do + context "true" do + let (:params) {{ :dev => 'present' }} + it { is_expected.to contain_package("python-dev").with_ensure('present') } + end + context "default/empty" do + it { is_expected.to contain_package("python-dev").with_ensure('absent') } + end + end + + describe "EPEL does not exist for Debian" do + context "default/empty" do + it { should_not contain_class('epel') } + end + end + + end + + context "on a Fedora 22 OS" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :osfamily => 'RedHat', + :operatingsystem => 'Fedora', + :operatingsystemrelease => '22', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + + describe "EPEL does not exist for Fedora" do + context "default/empty" do + it { should_not contain_class('epel') } + end + end + + end + + + context "on a Redhat 5 OS" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :osfamily => 'RedHat', + :operatingsystem => 'RedHat', + :operatingsystemrelease => '5', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_class("python::install") } + # Base debian packages. + it { is_expected.to contain_package("python") } + it { is_expected.to contain_package("python-dev").with_name("python-devel") } + it { is_expected.to contain_package("python-dev").with_alias("python-devel") } + it { is_expected.to contain_package("pip") } + it { is_expected.to contain_package("pip").with_name('python-pip') } + # Basic python packages (from pip) + it { is_expected.to contain_package("virtualenv")} + + describe "EPEL may be needed on EL" do + context "default/empty" do + it { should contain_class('epel') } + end + end + + describe "with python::dev" do + context "true" do + let (:params) {{ :dev => 'present' }} + it { is_expected.to contain_package("python-dev").with_ensure('present') } + end + context "empty/default" do + it { is_expected.to contain_package("python-dev").with_ensure('absent') } + end + end + + describe "with manage_gunicorn" do + context "true" do + let (:params) {{ :manage_gunicorn => true }} + it { is_expected.to contain_package("gunicorn") } + end + context "empty args" do + #let (:params) {{ :manage_gunicorn => '' }} + it { is_expected.to contain_package("gunicorn") } + end + context "false" do + let (:params) {{ :manage_gunicorn => false }} + it {is_expected.not_to contain_package("gunicorn")} + end + end + + describe "with python::provider" do + context "pip" do + let (:params) {{ :provider => 'pip' }} + + it { is_expected.to contain_package("virtualenv").with( + 'provider' => 'pip' + )} + it { is_expected.to contain_package("pip").with( + 'provider' => 'pip' + )} + end + + # python::provider + context "default" do + let (:params) {{ :provider => '' }} + it { is_expected.to contain_package("virtualenv")} + it { is_expected.to contain_package("pip")} + + describe "with python::virtualenv" do + context "true" do + let (:params) {{ :provider => '', :virtualenv => 'present' }} + it { is_expected.to contain_package("virtualenv").with_ensure('present') } + end + end + + describe "with python::virtualenv" do + context "default/empty" do + let (:params) {{ :provider => '' }} + it { is_expected.to contain_package("virtualenv").with_ensure('absent') } + end + end + end + end + + describe "with python::dev" do + context "true" do + let (:params) {{ :dev => 'present' }} + it { is_expected.to contain_package("python-dev").with_ensure('present') } + end + context "default/empty" do + it { is_expected.to contain_package("python-dev").with_ensure('absent') } + end + end + end + + context "on a Redhat 6 OS" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :osfamily => 'RedHat', + :operatingsystem => 'RedHat', + :operatingsystemmajrelease => '6', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_class("python::install") } + it { is_expected.to contain_package("pip").with_name('python-pip') } + end + + context "on a Redhat 7 OS" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :osfamily => 'RedHat', + :operatingsystem => 'RedHat', + :operatingsystemmajrelease => '7', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_class("python::install") } + it { is_expected.to contain_package("pip").with_name('python2-pip') } + end + + context "on a SLES 11 SP3" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :lsbdistcodename => nil, + :osfamily => 'Suse', + :operatingsystem => 'SLES', + :operatingsystemrelease => '11.3', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_class("python::install") } + # Base Suse packages. + it { is_expected.to contain_package("python") } + it { is_expected.to contain_package("python-dev").with_name("python-devel") } + it { is_expected.to contain_package("python-dev").with_alias("python-devel") } + it { is_expected.to contain_package("pip") } + # Basic python packages (from pip) + it { is_expected.to contain_package("virtualenv")} + + describe "with python::dev" do + context "true" do + let (:params) {{ :dev => 'present' }} + it { is_expected.to contain_package("python-dev").with_ensure('present') } + end + context "empty/default" do + it { is_expected.to contain_package("python-dev").with_ensure('absent') } + end + end + + describe "with manage_gunicorn" do + context "true" do + let (:params) {{ :manage_gunicorn => true }} + it { is_expected.to contain_package("gunicorn") } + end + context "empty args" do + #let (:params) {{ :manage_gunicorn => '' }} + it { is_expected.to contain_package("gunicorn") } + end + context "false" do + let (:params) {{ :manage_gunicorn => false }} + it {is_expected.not_to contain_package("gunicorn")} + end + end + + describe "with python::provider" do + context "pip" do + let (:params) {{ :provider => 'pip' }} + + it { is_expected.to contain_package("virtualenv").with( + 'provider' => 'pip' + )} + it { is_expected.to contain_package("pip").with( + 'provider' => 'pip' + )} + end + + # python::provider + context "default" do + let (:params) {{ :provider => '' }} + it { is_expected.to contain_package("virtualenv")} + it { is_expected.to contain_package("pip")} + + describe "with python::virtualenv" do + context "true" do + let (:params) {{ :provider => '', :virtualenv => 'present' }} + it { is_expected.to contain_package("virtualenv").with_ensure('present') } + end + end + + describe "with python::virtualenv" do + context "default/empty" do + let (:params) {{ :provider => '' }} + it { is_expected.to contain_package("virtualenv").with_ensure('absent') } + end + end + end + end + + describe "with python::dev" do + context "true" do + let (:params) {{ :dev => 'present' }} + it { is_expected.to contain_package("python-dev").with_ensure('present') } + end + context "default/empty" do + it { is_expected.to contain_package("python-dev").with_ensure('absent') } + end + end + + describe "EPEL does not exist on Suse" do + context "default/empty" do + it { should_not contain_class('epel') } + end + end + end + + context "on a Gentoo OS" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :lsbdistcodename => 'n/a', + :osfamily => 'Gentoo', + :operatingsystem => 'Gentoo', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_class("python::install") } + # Base debian packages. + it { is_expected.to contain_package("python") } + it { is_expected.to contain_package("pip").with({"category" => "dev-python"}) } + # Basic python packages (from pip) + it { is_expected.to contain_package("virtualenv")} + # Python::Dev + it { is_expected.not_to contain_package("python-dev") } + + describe "with manage_gunicorn" do + context "true" do + let (:params) {{ :manage_gunicorn => true }} + it { is_expected.to contain_package("gunicorn") } + end + context "empty args" do + #let (:params) {{ :manage_gunicorn => '' }} + it { is_expected.to contain_package("gunicorn") } + end + context "false" do + let (:params) {{ :manage_gunicorn => false }} + it {is_expected.not_to contain_package("gunicorn")} + end + end + + describe "with python::provider" do + context "pip" do + let (:params) {{ :provider => 'pip' }} + + it { is_expected.to contain_package("virtualenv").with( + 'provider' => 'pip' + )} + it { is_expected.to contain_package("pip").with( + 'provider' => 'pip' + )} + end + + # python::provider + context "default" do + let (:params) {{ :provider => '' }} + it { is_expected.to contain_package("virtualenv")} + it { is_expected.to contain_package("pip")} + + describe "with python::virtualenv" do + context "true" do + let (:params) {{ :provider => '', :virtualenv => 'present' }} + it { is_expected.to contain_package("virtualenv").with_ensure('present') } + end + end + + describe "with python::virtualenv" do + context "default/empty" do + let (:params) {{ :provider => '' }} + it { is_expected.to contain_package("virtualenv").with_ensure('absent') } + end + end + end + end + end + +end diff --git a/modules/utilities/unix/languages/python2/python/spec/defines/gunicorn_spec.rb b/modules/utilities/unix/languages/python2/python/spec/defines/gunicorn_spec.rb new file mode 100644 index 000000000..116ad414f --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/defines/gunicorn_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe 'python::gunicorn', :type => :define do + let(:title) { 'test-app' } + context 'on Debian OS' do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :lsbdistcodename => 'squeeze', + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '6', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :concat_basedir => '/dne', + } + end + + describe 'test-app with default parameter values' do + context 'configures test app with default parameter values' do + let(:params) { { :dir => '/srv/testapp' } } + it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(/--log-level=error/) } + end + + context 'test-app with custom log level' do + let(:params) { { :dir => '/srv/testapp', :log_level => 'info' } } + it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(/--log-level=info/) } + end + + context 'test-app with custom gunicorn preload arguments' do + let(:params) { { :dir => '/srv/testapp', :args => ['--preload'] } } + it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(/--preload/) } + end + end + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/defines/pip_spec.rb b/modules/utilities/unix/languages/python2/python/spec/defines/pip_spec.rb new file mode 100644 index 000000000..56216c599 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/defines/pip_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe 'python::pip', :type => :define do + let (:title) { 'rpyc' } + context "on Debian OS" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :lsbdistcodename => 'squeeze', + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '6', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :concat_basedir => '/dne', + } + end + + describe "virtualenv as" do + context "fails with non qualified path" do + let (:params) {{ :virtualenv => "venv" }} + it { is_expected.to raise_error(/"venv" is not an absolute path./) } + end + context "suceeds with qualified path" do + let (:params) {{ :virtualenv => "/opt/venv" }} + it { is_expected.to contain_exec("pip_install_rpyc").with_cwd('/opt/venv') } + end + context "defaults to system" do + let (:params) {{ }} + it { is_expected.to contain_exec("pip_install_rpyc").with_cwd('/') } + end + end + + describe "proxy as" do + context "defaults to empty" do + let (:params) {{ }} + it { should_not contain_exec("pip_install_rpyc").with_command(/--proxy/) } + end + context "does not add proxy to search command if set to latest and proxy is unset" do + let (:params) {{ :ensure => 'latest' }} + it { should_not contain_exec("pip_install_rpyc").with_command(/--proxy/) } + it { is_expected.to contain_exec("pip_install_rpyc").without_unless(/--proxy/) } + end + context "adds proxy to install command if proxy set" do + let (:params) {{ :proxy => "http://my.proxy:3128" }} + it { is_expected.to contain_exec("pip_install_rpyc").with_command("pip wheel --help > /dev/null 2>&1 && { pip wheel --version > /dev/null 2>&1 || wheel_support_flag='--no-use-wheel'; } ; { pip --log /tmp/pip.log install $wheel_support_flag --proxy=http://my.proxy:3128 rpyc || pip --log /tmp/pip.log install --proxy=http://my.proxy:3128 rpyc ;}") } + end + context "adds proxy to search command if set to latest" do + let (:params) {{ :proxy => "http://my.proxy:3128", :ensure => 'latest' }} + it { is_expected.to contain_exec("pip_install_rpyc").with_command("pip wheel --help > /dev/null 2>&1 && { pip wheel --version > /dev/null 2>&1 || wheel_support_flag='--no-use-wheel'; } ; { pip --log /tmp/pip.log install --upgrade $wheel_support_flag --proxy=http://my.proxy:3128 rpyc || pip --log /tmp/pip.log install --upgrade --proxy=http://my.proxy:3128 rpyc ;}") } + it { is_expected.to contain_exec("pip_install_rpyc").with_unless('pip search --proxy=http://my.proxy:3128 rpyc | grep -i INSTALLED.*latest') } + end + end + + describe 'index as' do + context 'defaults to empty' do + let (:params) {{ }} + it { should_not contain_exec('pip_install_rpyc').with_command(/--index-url/) } + end + context 'adds index to install command if index set' do + let (:params) {{ :index => 'http://www.example.com/simple/' }} + it { is_expected.to contain_exec('pip_install_rpyc').with_command("pip wheel --help > /dev/null 2>&1 && { pip wheel --version > /dev/null 2>&1 || wheel_support_flag='--no-use-wheel'; } ; { pip --log /tmp/pip.log install $wheel_support_flag --index-url=http://www.example.com/simple/ rpyc || pip --log /tmp/pip.log install --index-url=http://www.example.com/simple/ rpyc ;}") } + end + context 'adds index to search command if set to latest' do + let (:params) {{ :index => 'http://www.example.com/simple/', :ensure => 'latest' }} + it { is_expected.to contain_exec('pip_install_rpyc').with_command("pip wheel --help > /dev/null 2>&1 && { pip wheel --version > /dev/null 2>&1 || wheel_support_flag='--no-use-wheel'; } ; { pip --log /tmp/pip.log install --upgrade $wheel_support_flag --index-url=http://www.example.com/simple/ rpyc || pip --log /tmp/pip.log install --upgrade --index-url=http://www.example.com/simple/ rpyc ;}") } + end + end + + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/defines/pyvenv_spec.rb b/modules/utilities/unix/languages/python2/python/spec/defines/pyvenv_spec.rb new file mode 100644 index 000000000..f801a103d --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/defines/pyvenv_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe 'python::pyvenv', :type => :define do + let (:title) { '/opt/env' } + let (:facts) do + { + :lsbdistcodename => 'jessie', + :osfamily => 'Debian', + } + end + + it { + is_expected.to contain_file( '/opt/env') + is_expected.to contain_exec( "python_virtualenv_/opt/env").with_command("pyvenv --clear /opt/env") + } + + describe 'when ensure' do + context "is absent" do + let (:params) {{ + :ensure => 'absent' + }} + it { + is_expected.to contain_file( '/opt/env').with_ensure('absent').with_purge( true) + } + end + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/defines/requirements_spec.rb b/modules/utilities/unix/languages/python2/python/spec/defines/requirements_spec.rb new file mode 100644 index 000000000..daf564230 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/defines/requirements_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe 'python::requirements', :type => :define do + let (:title) { '/requirements.txt' } + context "on Debian OS" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :lsbdistcodename => 'squeeze', + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '6', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :concat_basedir => '/dne', + } + end + + describe "requirements as" do + context "/requirements.txt" do + let (:params) {{ :requirements => "/requirements.txt" }} + it { is_expected.to contain_file("/requirements.txt").with_mode('0644') } + end + context "/requirements.txt" do + let (:params) {{ :requirements => "/requirements.txt", :manage_requirements => false }} + it { is_expected.not_to contain_file("/requirements.txt") } + end + + describe "with owner" do + context "bob:bob" do + let (:params) {{ + :owner => 'bob', + :group => 'bob' + }} + it do + expect { + is_expected.to compile + }.to raise_error(/root user must be used when virtualenv is system/) + end + end + end + + describe "with owner" do + context "default" do + it { is_expected.to contain_file("/requirements.txt").with_owner('root').with_group('root') } + end + end + end + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/spec.opts b/modules/utilities/unix/languages/python2/python/spec/spec.opts new file mode 100644 index 000000000..91cd6427e --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/spec.opts @@ -0,0 +1,6 @@ +--format +s +--colour +--loadby +mtime +--backtrace diff --git a/modules/utilities/unix/languages/python2/python/spec/spec_helper.rb b/modules/utilities/unix/languages/python2/python/spec/spec_helper.rb new file mode 100644 index 000000000..d0e3996ce --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/spec_helper.rb @@ -0,0 +1,18 @@ +require 'puppetlabs_spec_helper/module_spec_helper' + +RSpec.configure do |config| + config.hiera_config = 'spec/fixtures/hiera/hiera.yaml' + config.before :each do + # Ensure that we don't accidentally cache facts and environment between + # test cases. This requires each example group to explicitly load the + # facts being exercised with something like + # Facter.collection.loader.load(:ipaddress) + Facter.clear + Facter.clear_messages + end + config.default_facts = { + :environment => 'rp_env', + :operatingsystemmajrelease => '6', + :osfamily => 'RedHat', + } +end diff --git a/modules/utilities/unix/languages/python2/python/spec/spec_helper_acceptance.rb b/modules/utilities/unix/languages/python2/python/spec/spec_helper_acceptance.rb new file mode 100644 index 000000000..c1bb62269 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/spec_helper_acceptance.rb @@ -0,0 +1,33 @@ +require 'beaker-rspec' + +UNSUPPORTED_PLATFORMS = [ 'windows' ] + +unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no' + hosts.each do |host| + if host.is_pe? + install_pe + else + install_puppet + end + end +end + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Readable test descriptions + c.formatter = :documentation + + # Configure all nodes in nodeset + c.before :suite do + # Install module and dependencies + hosts.each do |host| + shell("rm -rf /etc/puppet/modules/python/") + copy_module_to(host, :source => proj_root, :module_name => 'python') + shell("/bin/touch #{default['puppetpath']}/hiera.yaml") + on host, puppet('module install puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] } + on host, puppet('module install stahnma-epel'), { :acceptable_exit_codes => [0,1] } + end + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/unit/facter/pip_version_spec.rb b/modules/utilities/unix/languages/python2/python/spec/unit/facter/pip_version_spec.rb new file mode 100644 index 000000000..63a6187b4 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/unit/facter/pip_version_spec.rb @@ -0,0 +1,32 @@ +require "spec_helper" + +describe Facter::Util::Fact do + before { + Facter.clear + } + + let(:pip_version_output) { <<-EOS +pip 6.0.6 from /opt/boxen/homebrew/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pip-6.0.6-py2.7.egg (python 2.7) +EOS + } + + describe "pip_version" do + context 'returns pip version when pip present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("pip").returns(true) + Facter::Util::Resolution.expects(:exec).with("pip --version 2>&1").returns(pip_version_output) + expect(Facter.value(:pip_version)).to eq("6.0.6") + end + end + + context 'returns nil when pip not present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("pip").returns(false) + expect(Facter.value(:pip_version)).to eq(nil) + end + end + + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/unit/facter/python_release_spec.rb b/modules/utilities/unix/languages/python2/python/spec/unit/facter/python_release_spec.rb new file mode 100644 index 000000000..dc6895a46 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/unit/facter/python_release_spec.rb @@ -0,0 +1,98 @@ +require "spec_helper" + +describe Facter::Util::Fact do + before { + Facter.clear + } + + let(:python2_version_output) { <<-EOS +Python 2.7.9 +EOS + } + let(:python3_version_output) { <<-EOS +Python 3.3.0 +EOS + } + + describe "python_release" do + context 'returns Python release when `python` present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(true) + Facter::Util::Resolution.expects(:exec).with("python -V 2>&1").returns(python2_version_output) + expect(Facter.value(:python_release)).to eq("2.7") + end + end + + context 'returns nil when `python` not present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(false) + expect(Facter.value(:python_release)).to eq(nil) + end + end + + end + + describe "python2_release" do + context 'returns Python 2 release when `python` is present and Python 2' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(true) + Facter::Util::Resolution.expects(:exec).with("python -V 2>&1").returns(python2_version_output) + expect(Facter.value(:python2_release)).to eq('2.7') + end + end + + context 'returns Python 2 release when `python` is Python 3 and `python2` is present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(true) + Facter::Util::Resolution.expects(:exec).with("python -V 2>&1").returns(python3_version_output) + Facter::Util::Resolution.expects(:which).with("python2").returns(true) + Facter::Util::Resolution.expects(:exec).with("python2 -V 2>&1").returns(python2_version_output) + expect(Facter.value(:python2_release)).to eq('2.7') + end + end + + context 'returns nil when `python` is Python 3 and `python2` is absent' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(true) + Facter::Util::Resolution.expects(:exec).with("python -V 2>&1").returns(python3_version_output) + Facter::Util::Resolution.expects(:which).with("python2").returns(false) + expect(Facter.value(:python2_release)).to eq(nil) + end + end + + context 'returns nil when `python2` and `python` are absent' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(false) + Facter::Util::Resolution.expects(:which).with("python2").returns(false) + expect(Facter.value(:python2_release)).to eq(nil) + end + end + + end + + describe "python3_release" do + context 'returns Python 3 release when `python3` present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python3").returns(true) + Facter::Util::Resolution.expects(:exec).with("python3 -V 2>&1").returns(python3_version_output) + expect(Facter.value(:python3_release)).to eq("3.3") + end + end + + context 'returns nil when `python3` not present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python3").returns(false) + expect(Facter.value(:python3_release)).to eq(nil) + end + end + + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/unit/facter/python_version_spec.rb b/modules/utilities/unix/languages/python2/python/spec/unit/facter/python_version_spec.rb new file mode 100644 index 000000000..48ab1edec --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/unit/facter/python_version_spec.rb @@ -0,0 +1,98 @@ +require "spec_helper" + +describe Facter::Util::Fact do + before { + Facter.clear + } + + let(:python2_version_output) { <<-EOS +Python 2.7.9 +EOS + } + let(:python3_version_output) { <<-EOS +Python 3.3.0 +EOS + } + + describe "python_version" do + context 'returns Python version when `python` present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(true) + Facter::Util::Resolution.expects(:exec).with("python -V 2>&1").returns(python2_version_output) + expect(Facter.value(:python_version)).to eq("2.7.9") + end + end + + context 'returns nil when `python` not present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(false) + expect(Facter.value(:python_version)).to eq(nil) + end + end + + end + + describe "python2_version" do + context 'returns Python 2 version when `python` is present and Python 2' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(true) + Facter::Util::Resolution.expects(:exec).with("python -V 2>&1").returns(python2_version_output) + expect(Facter.value(:python2_version)).to eq('2.7.9') + end + end + + context 'returns Python 2 version when `python` is Python 3 and `python2` is present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(true) + Facter::Util::Resolution.expects(:exec).with("python -V 2>&1").returns(python3_version_output) + Facter::Util::Resolution.expects(:which).with("python2").returns(true) + Facter::Util::Resolution.expects(:exec).with("python2 -V 2>&1").returns(python2_version_output) + expect(Facter.value(:python2_version)).to eq('2.7.9') + end + end + + context 'returns nil when `python` is Python 3 and `python2` is absent' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(true) + Facter::Util::Resolution.expects(:exec).with("python -V 2>&1").returns(python3_version_output) + Facter::Util::Resolution.expects(:which).with("python2").returns(false) + expect(Facter.value(:python2_version)).to eq(nil) + end + end + + context 'returns nil when `python2` and `python` are absent' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python").returns(false) + Facter::Util::Resolution.expects(:which).with("python2").returns(false) + expect(Facter.value(:python2_version)).to eq(nil) + end + end + + end + + describe "python3_version" do + context 'returns Python 3 version when `python3` present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python3").returns(true) + Facter::Util::Resolution.expects(:exec).with("python3 -V 2>&1").returns(python3_version_output) + expect(Facter.value(:python3_version)).to eq("3.3.0") + end + end + + context 'returns nil when `python3` not present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("python3").returns(false) + expect(Facter.value(:python3_version)).to eq(nil) + end + end + + end +end diff --git a/modules/utilities/unix/languages/python2/python/spec/unit/facter/virtualenv_version_spec.rb b/modules/utilities/unix/languages/python2/python/spec/unit/facter/virtualenv_version_spec.rb new file mode 100644 index 000000000..b9aac90e3 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/spec/unit/facter/virtualenv_version_spec.rb @@ -0,0 +1,32 @@ +require "spec_helper" + +describe Facter::Util::Fact do + before { + Facter.clear + } + + let(:virtualenv_version_output) { <<-EOS +12.0.7 +EOS + } + + describe "virtualenv_version" do + context 'returns virtualenv version when virtualenv present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("virtualenv").returns(true) + Facter::Util::Resolution.expects(:exec).with("virtualenv --version 2>&1").returns(virtualenv_version_output) + expect(Facter.value(:virtualenv_version)).to eq("12.0.7") + end + end + + context 'returns nil when virtualenv not present' do + it do + Facter::Util::Resolution.stubs(:exec) + Facter::Util::Resolution.expects(:which).with("virtualenv").returns(false) + expect(Facter.value(:virtualenv_version)).to eq(nil) + end + end + + end +end diff --git a/modules/utilities/unix/languages/python2/python/templates/gunicorn.erb b/modules/utilities/unix/languages/python2/python/templates/gunicorn.erb new file mode 100644 index 000000000..07605ade1 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/templates/gunicorn.erb @@ -0,0 +1,61 @@ +CONFIG = { +<% if @mode == 'django' -%> + 'mode': 'django', +<% else -%> + 'mode': 'wsgi', +<% end -%> +<% if @virtualenv -%> + 'environment': { +<% if @osenv -%><% @osenv.sort.each do |key, value| -%> + '<%= key %>': '<%= value %>', +<% end -%><% end -%> +<% if @environment -%> + 'ENVIRONMENT': '<%= @environment %>', +<% end -%> + 'PYTHONPATH': '<%= @virtualenv %>' + }, +<% end -%> + 'working_dir': '<%= @dir %>', + 'user': '<%= @owner %>', + 'group': '<%= @group %>', +<% if @virtualenv -%> + 'python': '<%= @virtualenv %>/bin/python', +<% else -%> + 'python': '/usr/bin/python', +<% end -%> + 'args': ( +<% if @args.any? -%> +<% for arg in @args do -%> + '<%= arg %>', +<% end -%> +<% end -%> +<% if !@virtualenv and !@bind -%> + '--bind=unix:/tmp/gunicorn-<%= @name %>.socket', +<% elsif @virtualenv and !@bind -%> + '--bind=unix:<%= @virtualenv %>/<%= @name %>.socket', +<% else -%> + '--bind=<%= @bind %>', +<% end -%> +<% if @workers -%> + '--workers=<%= @workers %>', +<% else -%> + '--workers=<%= @processorcount.to_i*2 + 1 %>', +<% end -%> + '--timeout=<%= @timeout %>', +<% if @access_log_format -%> + '--access-logformat=<%= @access_log_format %>', +<% end -%> +<% if @accesslog -%> + '--access-logfile=<%= @accesslog %>', +<% end -%> +<% if @errorlog -%> + '--error-logfile=<%= @errorlog %>', +<% end -%> +<% if @log_level %> + '--log-level=<%= @log_level %>', +<% end -%> +<% if @mode != 'django' -%> + '<%= @appmodule %>', +<% end -%> + ), +} diff --git a/modules/utilities/unix/languages/python2/python/templates/inifile.erb b/modules/utilities/unix/languages/python2/python/templates/inifile.erb new file mode 100644 index 000000000..47ae7138b --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/templates/inifile.erb @@ -0,0 +1,8 @@ +# this file is managed by puppet +<%- @config.sort.map do |section,conf| -%> +[<%= section -%>] +<%- conf.sort.map do |key,value| -%> +<%= key %> = <%= value %> +<%- end -%> + +<%- end -%> diff --git a/modules/utilities/unix/languages/python2/python/templates/tests/gunicorn.pp b/modules/utilities/unix/languages/python2/python/templates/tests/gunicorn.pp new file mode 100644 index 000000000..d95f6d0f9 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/templates/tests/gunicorn.pp @@ -0,0 +1,20 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} + +python::gunicorn { 'vhost': + ensure => present, + virtualenv => '/var/www/project1', + mode => 'wsgi', + dir => '/var/www/project1/current', + bind => 'unix:/tmp/gunicorn.socket', + environment => 'prod', + appmodule => 'app:app', + osenv => { + 'DBHOST' => 'dbserver.example.com' + }, + timeout => 30, + template => 'python/gunicorn.erb', +} diff --git a/modules/utilities/unix/languages/python2/python/templates/tests/init.pp b/modules/utilities/unix/languages/python2/python/templates/tests/init.pp new file mode 100644 index 000000000..4c51cc80e --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/templates/tests/init.pp @@ -0,0 +1,5 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} diff --git a/modules/utilities/unix/languages/python2/python/templates/tests/pip.pp b/modules/utilities/unix/languages/python2/python/templates/tests/pip.pp new file mode 100644 index 000000000..f0db17280 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/templates/tests/pip.pp @@ -0,0 +1,10 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} + +python::pip { 'flask': + virtualenv => '/var/www/project1', + proxy => 'http://proxy.domain.com:3128', +} diff --git a/modules/utilities/unix/languages/python2/python/templates/tests/pyvenv.pp b/modules/utilities/unix/languages/python2/python/templates/tests/pyvenv.pp new file mode 100644 index 000000000..8cd0cbce0 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/templates/tests/pyvenv.pp @@ -0,0 +1,12 @@ +class { 'python': + pip => false, + version => '3', +} + +python::pyvenv { '/opt/uwsgi': +} + +python::pip { 'uwsgi': + ensure => 'latest', + virtualenv => '/opt/uwsgi' +} diff --git a/modules/utilities/unix/languages/python2/python/templates/tests/requirements.pp b/modules/utilities/unix/languages/python2/python/templates/tests/requirements.pp new file mode 100644 index 000000000..90dccd8bb --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/templates/tests/requirements.pp @@ -0,0 +1,16 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} + +python::requirements { '/var/www/project1/requirements.txt': + virtualenv => '/var/www/project1', + proxy => 'http://proxy.domain.com:3128', +} + +python::requirements { '/var/www/project1/requirements.txt': + virtualenv => 'system', + proxy => 'http://proxy.domain.com:3128', + timeout => 2400, +} diff --git a/modules/utilities/unix/languages/python2/python/templates/tests/virtualenv.pp b/modules/utilities/unix/languages/python2/python/templates/tests/virtualenv.pp new file mode 100644 index 000000000..da5ab57c5 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/templates/tests/virtualenv.pp @@ -0,0 +1,13 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} + +python::virtualenv { '/var/www/project1': + ensure => present, + version => 'system', + requirements => '/var/www/project1/requirements.txt', + proxy => 'http://proxy.domain.com:3128', + systempkgs => true, +} diff --git a/modules/utilities/unix/languages/python2/python/tests/gunicorn.pp b/modules/utilities/unix/languages/python2/python/tests/gunicorn.pp new file mode 100644 index 000000000..d95f6d0f9 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/tests/gunicorn.pp @@ -0,0 +1,20 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} + +python::gunicorn { 'vhost': + ensure => present, + virtualenv => '/var/www/project1', + mode => 'wsgi', + dir => '/var/www/project1/current', + bind => 'unix:/tmp/gunicorn.socket', + environment => 'prod', + appmodule => 'app:app', + osenv => { + 'DBHOST' => 'dbserver.example.com' + }, + timeout => 30, + template => 'python/gunicorn.erb', +} diff --git a/modules/utilities/unix/languages/python2/python/tests/init.pp b/modules/utilities/unix/languages/python2/python/tests/init.pp new file mode 100644 index 000000000..4c51cc80e --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/tests/init.pp @@ -0,0 +1,5 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} diff --git a/modules/utilities/unix/languages/python2/python/tests/pip.pp b/modules/utilities/unix/languages/python2/python/tests/pip.pp new file mode 100644 index 000000000..f0db17280 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/tests/pip.pp @@ -0,0 +1,10 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} + +python::pip { 'flask': + virtualenv => '/var/www/project1', + proxy => 'http://proxy.domain.com:3128', +} diff --git a/modules/utilities/unix/languages/python2/python/tests/pyvenv.pp b/modules/utilities/unix/languages/python2/python/tests/pyvenv.pp new file mode 100644 index 000000000..8cd0cbce0 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/tests/pyvenv.pp @@ -0,0 +1,12 @@ +class { 'python': + pip => false, + version => '3', +} + +python::pyvenv { '/opt/uwsgi': +} + +python::pip { 'uwsgi': + ensure => 'latest', + virtualenv => '/opt/uwsgi' +} diff --git a/modules/utilities/unix/languages/python2/python/tests/requirements.pp b/modules/utilities/unix/languages/python2/python/tests/requirements.pp new file mode 100644 index 000000000..90dccd8bb --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/tests/requirements.pp @@ -0,0 +1,16 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} + +python::requirements { '/var/www/project1/requirements.txt': + virtualenv => '/var/www/project1', + proxy => 'http://proxy.domain.com:3128', +} + +python::requirements { '/var/www/project1/requirements.txt': + virtualenv => 'system', + proxy => 'http://proxy.domain.com:3128', + timeout => 2400, +} diff --git a/modules/utilities/unix/languages/python2/python/tests/virtualenv.pp b/modules/utilities/unix/languages/python2/python/tests/virtualenv.pp new file mode 100644 index 000000000..da5ab57c5 --- /dev/null +++ b/modules/utilities/unix/languages/python2/python/tests/virtualenv.pp @@ -0,0 +1,13 @@ +class { 'python': + version => 'system', + dev => true, + virtualenv => true, +} + +python::virtualenv { '/var/www/project1': + ensure => present, + version => 'system', + requirements => '/var/www/project1/requirements.txt', + proxy => 'http://proxy.domain.com:3128', + systempkgs => true, +} diff --git a/modules/utilities/unix/languages/python3/python/lib b/modules/utilities/unix/languages/python3/python/lib new file mode 120000 index 000000000..d9af22bcd --- /dev/null +++ b/modules/utilities/unix/languages/python3/python/lib @@ -0,0 +1 @@ +/home/thomashaw/git/SecGen/modules/utilities/unix/languages/python/lib \ No newline at end of file diff --git a/modules/utilities/unix/languages/python3/python/manifests b/modules/utilities/unix/languages/python3/python/manifests new file mode 120000 index 000000000..db11cc287 --- /dev/null +++ b/modules/utilities/unix/languages/python3/python/manifests @@ -0,0 +1 @@ +/home/thomashaw/git/SecGen/modules/utilities/unix/languages/python/manifests \ No newline at end of file diff --git a/modules/utilities/unix/languages/python3/python/python.pp b/modules/utilities/unix/languages/python3/python/python.pp new file mode 100644 index 000000000..7bb2c4b0a --- /dev/null +++ b/modules/utilities/unix/languages/python3/python/python.pp @@ -0,0 +1,7 @@ +class { 'python': + version => 'python3', + pip => 'present', + dev => 'absent', + virtualenv => 'absent', + gunicorn => 'absent', +} \ No newline at end of file diff --git a/modules/utilities/unix/languages/python3/python/secgen_metadata.xml b/modules/utilities/unix/languages/python3/python/secgen_metadata.xml new file mode 100644 index 000000000..4b9ebc7f5 --- /dev/null +++ b/modules/utilities/unix/languages/python3/python/secgen_metadata.xml @@ -0,0 +1,25 @@ + + + + Python3 programming language install + Thomas Shaw + stankevich + Apache v2 + An installation of Python + + language + python + linux + + + https://forge.puppet.com/stankevich/python + + ruby + Apache v2 + + + update + + \ No newline at end of file diff --git a/modules/utilities/unix/languages/python3/python/spec b/modules/utilities/unix/languages/python3/python/spec new file mode 120000 index 000000000..750e485f3 --- /dev/null +++ b/modules/utilities/unix/languages/python3/python/spec @@ -0,0 +1 @@ +/home/thomashaw/git/SecGen/modules/utilities/unix/languages/python/spec \ No newline at end of file diff --git a/modules/utilities/unix/languages/python3/python/templates b/modules/utilities/unix/languages/python3/python/templates new file mode 120000 index 000000000..d8d525e74 --- /dev/null +++ b/modules/utilities/unix/languages/python3/python/templates @@ -0,0 +1 @@ +/home/thomashaw/git/SecGen/modules/utilities/unix/languages/python/templates \ No newline at end of file diff --git a/modules/utilities/unix/languages/python3/python/tests b/modules/utilities/unix/languages/python3/python/tests new file mode 120000 index 000000000..d8d525e74 --- /dev/null +++ b/modules/utilities/unix/languages/python3/python/tests @@ -0,0 +1 @@ +/home/thomashaw/git/SecGen/modules/utilities/unix/languages/python/templates \ No newline at end of file diff --git a/modules/vulnerabilities/unix/ctf/python2_challenge_example/secgen_metadata.xml b/modules/vulnerabilities/unix/ctf/python2_challenge_example/secgen_metadata.xml index 3fc6c29f8..adaa58c3f 100644 --- a/modules/vulnerabilities/unix/ctf/python2_challenge_example/secgen_metadata.xml +++ b/modules/vulnerabilities/unix/ctf/python2_challenge_example/secgen_metadata.xml @@ -55,7 +55,7 @@ - utilities/unix/languages/python + utilities/unix/languages/python2/python diff --git a/modules/vulnerabilities/unix/ctf/python3_challenge_example/manifests/install.pp b/modules/vulnerabilities/unix/ctf/python3_challenge_example/manifests/install.pp new file mode 100644 index 000000000..b81b06b48 --- /dev/null +++ b/modules/vulnerabilities/unix/ctf/python3_challenge_example/manifests/install.pp @@ -0,0 +1,45 @@ +class python3_challenge_example::install { + $secgen_params = secgen_functions::get_parameters($::base64_inputs_file) + $group = $secgen_params['group'] + $script_data = $secgen_params['script_data'] + + if $secgen_params['account'][0] and $secgen_params['account'][0] != '' { + $account = parsejson($secgen_params['account'][0]) + } else { + $account = undef + } + + if $secgen_params['storage_directory'] and $secgen_params['storage_directory'][0] { + $storage_dir = $secgen_params['storage_directory'][0] + } else { + $storage_dir = undef + } + + if $group { + ::secgen_functions::install_setgid_script { 'python3_challenge_example': + source_module_name => $module_name, + challenge_name => $secgen_params['challenge_name'][0], + script_name => 'test.py', + script_data => $script_data[0], + group => $group[0], + account => $account, + flag => $secgen_params['flag'][0], + flag_name => 'flag', + storage_dir => $storage_dir, + strings_to_leak => $secgen_params['strings_to_leak'], + } + } else { + ::secgen_functions::install_setuid_root_script { 'python3_challenge_example': + source_module_name => $module_name, + challenge_name => $secgen_params['challenge_name'][0], + script_name => 'test.py', + script_data => $script_data[0], + account => $account, + flag => $secgen_params['flag'][0], + flag_name => 'flag', + storage_dir => $storage_dir, + strings_to_leak => $secgen_params['strings_to_leak'], + } + } + +} diff --git a/modules/vulnerabilities/unix/ctf/python3_challenge_example/python3_challenge_example.pp b/modules/vulnerabilities/unix/ctf/python3_challenge_example/python3_challenge_example.pp new file mode 100644 index 000000000..41203bed3 --- /dev/null +++ b/modules/vulnerabilities/unix/ctf/python3_challenge_example/python3_challenge_example.pp @@ -0,0 +1 @@ +include python3_challenge_example::install diff --git a/modules/vulnerabilities/unix/ctf/python3_challenge_example/secgen_metadata.xml b/modules/vulnerabilities/unix/ctf/python3_challenge_example/secgen_metadata.xml new file mode 100644 index 000000000..12da317b6 --- /dev/null +++ b/modules/vulnerabilities/unix/ctf/python3_challenge_example/secgen_metadata.xml @@ -0,0 +1,61 @@ + + + + python3 Challenge Example + Thomas Shaw + MIT + python3 challenge example + + ctf_challenge + script_challenge + none + local + linux + + + challenge_name + script_data + account + flag + + storage_directory + + group + + python3_script_example + + + + + + + + challenges + + + password + + + + + + + + python3_script_example + + + + utilities/unix/system/accounts + + + + utilities/unix/system/binary_script_container + + + + utilities/unix/languages/python3/python + + +