diff --git a/.gitignore b/.gitignore index d7f3efa..6bf6e4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /.chef/encrypted_data_bag_secret /.bundle/ +/.vagrant/ +/nodes/vagrant-node.json diff --git a/Batali b/Batali index 2e532a8..f9723d0 100644 --- a/Batali +++ b/Batali @@ -14,10 +14,10 @@ Batali.define do ref: 'relax_dependencies' cookbook 'postfix' cookbook 'unattended-upgrades' - cookbook 'application_nodejs', - git: 'https://github.com/67p/application_nodejs.git', - ref: 'master' - cookbook 'application', '4.1.6' + cookbook 'application' + cookbook 'application_javascript' + cookbook 'application_ruby' + cookbook 'application_git' cookbook 'users' cookbook 'sudo' cookbook 'hostname' @@ -29,6 +29,7 @@ Batali.define do cookbook 'nginx' cookbook 'build-essential' cookbook 'mysql' + cookbook 'postgresql', '~> 6.1' cookbook 'database' cookbook 'mysql2_chef_gem' cookbook 'omnibus_updater' diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..ceed2fd --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,95 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure(2) do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://atlas.hashicorp.com/search. + config.vm.box = "bento/ubuntu-15.04" + + + config.vm.provider "virtualbox" do |vb| + # Customize the amount of memory on the VM: + vb.memory = "1024" + end + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + # config.vm.synced_folder "../data", "/vagrant_data" + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + # end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies + # such as FTP and Heroku are also available. See the documentation at + # https://docs.vagrantup.com/v2/push/atlas.html for more information. + # config.push.define "atlas" do |push| + # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" + # end + + # Enable provisioning with a shell script. Additional provisioners such as + # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the + # documentation for more information about their specific syntax and use. + # config.vm.provision "shell", inline: <<-SHELL + # sudo apt-get update + # sudo apt-get install -y apache2 + # SHELL + + config.vm.provision :chef_zero do |chef| + chef.cookbooks_path = ['./cookbooks', './site-cookbooks'] + chef.data_bags_path = './data_bags' + chef.roles_path = './roles' + chef.node_name = "vagrant-node" + chef.nodes_path = './nodes' + chef.environments_path = './environments' + chef.encrypted_data_bag_secret_key_path = '.chef/encrypted_data_bag_secret' + chef.environment = 'development' + # chef.add_recipe 'kosmos-wordpress' + # chef.add_recipe 'sockethub' + chef.add_recipe 'kosmos-mastodon' + # chef.add_recipe 'kosmos-mastodon::nginx' + # chef.add_recipe '5apps-hubot::xmpp_botka' + # chef.add_recipe 'kosmos-hubot' + + end +end diff --git a/batali.manifest b/batali.manifest index 47b4f56..1ed1cd4 100644 --- a/batali.manifest +++ b/batali.manifest @@ -36,7 +36,7 @@ "version": "0.2.0", "source": { "url": "https://github.com/67P/mediawiki-cookbook.git", - "ref": "b76104ab975ed95e238c10ae49722f0dacd7c0b2", + "ref": "41d3c5129b5a6cd9c473e99339885bc1feac5d57", "type": "Batali::Source::Git", "subdirectory": null } @@ -325,29 +325,6 @@ "version": "4.0.9" } }, - { - "name": "postgresql", - "dependencies": [ - [ - "apt", - ">= 1.9.0" - ], - [ - "build-essential", - ">= 0.0.0" - ], - [ - "openssl", - "~> 4.0" - ] - ], - "version": "4.0.0", - "source": { - "type": "Batali::Source::Site", - "url": "https://supermarket.chef.io:443/api/v1/cookbooks/postgresql/versions/4.0.0/download", - "version": "4.0.0" - } - }, { "name": "apt", "dependencies": [ @@ -612,7 +589,7 @@ ], [ "mysql2_chef_gem", - "~> 1.0.1" + ">= 1.0.1" ], [ "build-essential", @@ -642,7 +619,7 @@ "version": "3.0.0", "source": { "url": "https://github.com/67P/wordpress-cookbook.git", - "ref": "bc6a108fcfb05c3fafd903bcf81ac33617e6cef9", + "ref": "d6401db517476e6f3ab36aa92dfc0f5ed6a8a264", "type": "Batali::Source::Git", "subdirectory": null } @@ -698,105 +675,234 @@ "version": "0.1.2" } }, - { - "name": "application_nodejs", - "dependencies": [ - [ - "nodejs", - "> 0" - ], - [ - "application", - "> 0" - ] - ], - "version": "2.0.1", - "source": { - "url": "https://github.com/67p/application_nodejs.git", - "ref": "0c3494b0cae87bd1e9cbf360e91f1a290b517a66", - "type": "Batali::Source::Git", - "subdirectory": null - } - }, - { - "name": "nodejs", - "dependencies": [ - [ - "yum-epel", - ">= 0.0.0" - ], - [ - "build-essential", - ">= 0.0.0" - ], - [ - "ark", - ">= 0.0.0" - ], - [ - "apt", - ">= 2.9.1" - ], - [ - "homebrew", - ">= 0.0.0" - ] - ], - "version": "3.0.0", - "source": { - "type": "Batali::Source::Site", - "url": "https://supermarket.chef.io:443/api/v1/cookbooks/nodejs/versions/3.0.0/download", - "version": "3.0.0" - } - }, - { - "name": "ark", - "dependencies": [ - [ - "build-essential", - ">= 0.0.0" - ], - [ - "windows", - ">= 0.0.0" - ], - [ - "seven_zip", - ">= 0.0.0" - ] - ], - "version": "2.2.1", - "source": { - "type": "Batali::Source::Site", - "url": "https://supermarket.chef.io:443/api/v1/cookbooks/ark/versions/2.2.1/download", - "version": "2.2.1" - } - }, - { - "name": "homebrew", - "dependencies": [ - [ - "build-essential", - ">= 2.1.2" - ] - ], - "version": "2.0.5", - "source": { - "type": "Batali::Source::Site", - "url": "https://supermarket.chef.io:443/api/v1/cookbooks/homebrew/versions/2.0.5/download", - "version": "2.0.5" - } - }, { "name": "application", + "dependencies": [ + [ + "poise", + "~> 2.4" + ], + [ + "poise-service", + "~> 1.0" + ] + ], + "version": "5.1.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/application/versions/5.1.0/download", + "version": "5.1.0" + } + }, + { + "name": "poise", "dependencies": [ ], - "version": "4.1.6", + "version": "2.7.2", "source": { "type": "Batali::Source::Site", - "url": "https://supermarket.chef.io:443/api/v1/cookbooks/application/versions/4.1.6/download", - "version": "4.1.6" + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/poise/versions/2.7.2/download", + "version": "2.7.2" + } + }, + { + "name": "poise-service", + "dependencies": [ + [ + "poise", + "~> 2.0" + ] + ], + "version": "1.4.2", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/poise-service/versions/1.4.2/download", + "version": "1.4.2" + } + }, + { + "name": "application_javascript", + "dependencies": [ + [ + "poise", + "~> 2.0" + ], + [ + "application", + "~> 5.0" + ], + [ + "poise-javascript", + "~> 1.0" + ], + [ + "poise-service", + "~> 1.0" + ] + ], + "version": "1.0.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/application_javascript/versions/1.0.0/download", + "version": "1.0.0" + } + }, + { + "name": "poise-javascript", + "dependencies": [ + [ + "poise", + "~> 2.0" + ], + [ + "poise-languages", + "~> 2.0" + ] + ], + "version": "1.1.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/poise-javascript/versions/1.1.0/download", + "version": "1.1.0" + } + }, + { + "name": "poise-languages", + "dependencies": [ + [ + "poise", + "~> 2.5" + ], + [ + "poise-archive", + "~> 1.0" + ] + ], + "version": "2.1.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/poise-languages/versions/2.1.0/download", + "version": "2.1.0" + } + }, + { + "name": "poise-archive", + "dependencies": [ + [ + "poise", + "~> 2.6" + ] + ], + "version": "1.4.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/poise-archive/versions/1.4.0/download", + "version": "1.4.0" + } + }, + { + "name": "application_ruby", + "dependencies": [ + [ + "poise-service", + "~> 1.0" + ], + [ + "poise", + "~> 2.0" + ], + [ + "application", + "~> 5.0" + ], + [ + "poise-ruby", + "~> 2.1" + ] + ], + "version": "4.0.1", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/application_ruby/versions/4.0.1/download", + "version": "4.0.1" + } + }, + { + "name": "poise-ruby", + "dependencies": [ + [ + "poise", + "~> 2.0" + ], + [ + "poise-languages", + "~> 2.0" + ] + ], + "version": "2.2.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/poise-ruby/versions/2.2.0/download", + "version": "2.2.0" + } + }, + { + "name": "application_git", + "dependencies": [ + [ + "git", + ">= 0.0.0" + ], + [ + "poise", + "~> 2.0" + ], + [ + "application", + "~> 5.0" + ] + ], + "version": "1.1.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/application_git/versions/1.1.0/download", + "version": "1.1.0" + } + }, + { + "name": "git", + "dependencies": [ + [ + "build-essential", + ">= 0.0.0" + ], + [ + "dmg", + ">= 0.0.0" + ], + [ + "yum-epel", + ">= 0.0.0" + ] + ], + "version": "6.0.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/git/versions/6.0.0/download", + "version": "6.0.0" + } + }, + { + "name": "dmg", + "dependencies": [ + + ], + "version": "3.1.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/dmg/versions/3.1.0/download", + "version": "3.1.0" } }, { @@ -896,6 +1002,29 @@ "version": "2.5.4" } }, + { + "name": "postgresql", + "dependencies": [ + [ + "compat_resource", + ">= 12.16.3" + ], + [ + "build-essential", + ">= 2.0.0" + ], + [ + "openssl", + ">= 4.0" + ] + ], + "version": "6.1.1", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/postgresql/versions/6.1.1/download", + "version": "6.1.1" + } + }, { "name": "omnibus_updater", "dependencies": [ @@ -920,6 +1049,75 @@ "version": "0.2.0" } }, + { + "name": "nodejs", + "dependencies": [ + [ + "yum-epel", + ">= 0.0.0" + ], + [ + "build-essential", + ">= 0.0.0" + ], + [ + "ark", + ">= 0.0.0" + ], + [ + "apt", + ">= 2.9.1" + ], + [ + "homebrew", + ">= 0.0.0" + ] + ], + "version": "3.0.0", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/nodejs/versions/3.0.0/download", + "version": "3.0.0" + } + }, + { + "name": "ark", + "dependencies": [ + [ + "build-essential", + ">= 0.0.0" + ], + [ + "windows", + ">= 0.0.0" + ], + [ + "seven_zip", + ">= 0.0.0" + ] + ], + "version": "2.2.1", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/ark/versions/2.2.1/download", + "version": "2.2.1" + } + }, + { + "name": "homebrew", + "dependencies": [ + [ + "build-essential", + ">= 2.1.2" + ] + ], + "version": "2.0.5", + "source": { + "type": "Batali::Source::Site", + "url": "https://supermarket.chef.io:443/api/v1/cookbooks/homebrew/versions/2.0.5/download", + "version": "2.0.5" + } + }, { "name": "logrotate", "dependencies": [ diff --git a/cookbooks/application/CHANGELOG.md b/cookbooks/application/CHANGELOG.md index 44aab88..1a4498d 100644 --- a/cookbooks/application/CHANGELOG.md +++ b/cookbooks/application/CHANGELOG.md @@ -1,103 +1,96 @@ -application Cookbook CHANGELOG -======================= -This file is used to list changes made in each version of the application cookbook. +# Application Changelog +## v5.1.0 -v4.1.6 ------- -- Support for Chef 12. -- Add `strict_ssh` option to enable host key checking. -- Add `keep_releases` option to control number of releases to keep. -- Allow passing a path to a file for `deploy_key`. +* Add `application_cookbook_file`, `application_file`, and `application_template resources. -v4.1.4 ------- -### Bug -- **[COOK-3343](https://tickets.opscode.com/browse/COOK-3343)** - Can't parse release candidate version number +## v5.0.0 +* Massive rewrite on top of newer Chef patterns. See the 5.0 README for details. -v4.1.2 ------- -### Bug -- **[COOK-3343](https://tickets.opscode.com/browse/COOK-3343)** - Can't parse release candidate version number +## v4.1.6 +* Support for Chef 12. +* Add `strict_ssh` option to enable host key checking. +* Add `keep_releases` option to control number of releases to keep. +* Allow passing a path to a file for `deploy_key`. -v4.1.0 ------- -### Bug -- [COOK-3343] - Can't parse release candidate version number +## v4.1.4 +* [COOK-3343](https://tickets.opscode.com/browse/COOK-3343) - Can't parse release candidate version number. -v4.0.0 ------- -### Breaking -- Removes compatability with Chef 10 +## v4.1.2 + +* [COOK-3343](https://tickets.opscode.com/browse/COOK-3343) - Can't parse release candidate version number. + +## v4.1.0 + +* [COOK-3343] - Can't parse release candidate version number. + +## v4.0.0 + +* Removes compatability with Chef 10. +* [COOK-3564](https://tickets.opscode.com/browse/COOK-3564) - Replace calls to `Chef::Mixin::RecipeDefinitionDSLCore`. + +## v3.0.0 + +* [COOK-3306]: Multiple Memory Leaks in Application Cookbook. + +## v2.0.4 + +* [COOK-2812]: application cookbook doesn't allow to specify a block as `restart_command`. + +## v2.0.2 + +* [COOK-2537]: Provide proper `respond_to` behavior when using `method_missing`. +* [COOK-2713]: application resource should Allow sub-resource attributes to propogate up. ### Improvement -- **[COOK-3564](https://tickets.opscode.com/browse/COOK-3564)** - Replace calls to `Chef::Mixin::RecipeDefinitionDSLCore` +* [COOK-2597]: Allow customization for `shallow_clone` when doing a git deploy. -v3.0.0 ------- -### Bug -- [COOK-3306]: Multiple Memory Leaks in Application Cookbook +## v2.0.0 -v2.0.4 ------- -### Bug -- [COOK-2812]: application cookbook doesn't allow to specify a block as `restart_command` - -v2.0.2 ------- -### Bug -- [COOK-2537]: Provide proper `respond_to` behavior when using `method_missing` -- [COOK-2713]: application resource should Allow sub-resource attributes to propogate up - -### Improvement -- [COOK-2597]: Allow customization for `shallow_clone` when doing a git deploy - -v2.0.0 ------- This release is incompatible with previous releases (hence major version change). The recipes used in older versions are deprecated and completely removed. See README.md for further detail. -- [COOK-1673] - `deploy_revision` in the application cookbook gives an argument error -- [COOK-1820] - Application cookbook: remove deprecated recipes +* [COOK-1673] - `deploy_revision` in the application cookbook gives an argument error. +* [COOK-1820] - Application cookbook: remove deprecated recipes. -v1.0.4 ------- -- [COOK-1567] - Add git submodules to application cookbook +## v1.0.4 -v1.0.2 ------- -- [COOK-1312] - string callbacks fail with method not found (really included this time) -- [COOK-1332] - add `release_path` and `shared_path` methods -- [COOK-1333] - add example for running migrations -- [COOK-1360] - fix minor typos in README -- [COOK-1374] - use runit attributes in unicorn run script +* [COOK-1567] - Add git submodules to application cookbook. + +## v1.0.2 + +* [COOK-1312] - string callbacks fail with method not found (really included this time). +* [COOK-1332] - add `release_path` and `shared_path` methods. +* [COOK-1333] - add example for running migrations. +* [COOK-1360] - fix minor typos in README. +* [COOK-1374] - use runit attributes in unicorn run script. + +## v1.0.0 -v1.0.0 ------- This release introduces the LWRP for application deployment, as well as other improvements. The recipes will be deprecated in August 2012 as indicated by their warning messages and in the README.md. -- [COOK-634] - Implement LWRP for application deployment -- [COOK-1116] - use other SCMs than git -- [COOK-1252] - add `:force_deploy` that maps to corresponding action of deploy resource -- [COOK-1253] - fix rollback error -- [COOK-1312] - string callbacks fail with method not found -- [COOK-1313] - implicit file based hooks aren't invoked -- [COOK-1318] - Create `to_ary` method to resolve issue in resources() lookup on "application[foo]" resources +* [COOK-634] - Implement LWRP for application deployment. +* [COOK-1116] - use other SCMs than git. +* [COOK-1252] - add `:force_deploy` that maps to corresponding action of deploy resource. +* [COOK-1253] - fix rollback error. +* [COOK-1312] - string callbacks fail with method not found. +* [COOK-1313] - implicit file based hooks aren't invoked. +* [COOK-1318] - Create `to_ary` method to resolve issue in resources() lookup on "application[foo]" resources. -v0.99.14 --------- -- [COOK-1065] - use pip in virtualenv during deploy +## v0.99.14 -v0.99.12 --------- -- [COOK-606] application cookbook deployment recipes should use ipaddress instead of fqdn +* [COOK-1065] - use pip in virtualenv during deploy. -v0.99.11 --------- -- make the `_default` `chef_environment` look like production rails env +## v0.99.12 -v0.99.10 --------- -- Use Chef 0.10's `node.chef_environment` instead of `node['app_environment']`. +* [COOK-606] application cookbook deployment recipes should use ipaddress instead of fqdn. + +## v0.99.11 + +* make the `_default` `chef_environment` look like production rails env. + +## v0.99.10 + +* Use Chef 0.10's `node.chef_environment` instead of `node['app_environment']`. diff --git a/cookbooks/application/README.md b/cookbooks/application/README.md index b0d2c4e..6089ca3 100644 --- a/cookbooks/application/README.md +++ b/cookbooks/application/README.md @@ -1,206 +1,259 @@ -Application cookbook -==================== -This cookbook is designed to be able to describe and deploy web applications. It provides the basic infrastructure; other cookbooks are required to support specific combinations of frameworks and application servers. The following cookbooks are available at this time: +# Application cookbook -- [application_java](https://github.com/opscode-cookbooks/application_java) (Java and Tomcat) -- [application_nginx](https://github.com/opscode-cookbooks/application_nginx) (nginx reverse proxy) -- [application_php](https://github.com/opscode-cookbooks/application_php) (PHP with `mod_php_apache2`) -- [application_python](https://github.com/opscode-cookbooks/application_python) (Django with Gunicorn) -- [application_ruby](https://github.com/opscode-cookbooks/application_ruby) (Rails with Passenger or Unicorn) +[![Build Status](https://img.shields.io/travis/poise/application.svg)](https://travis-ci.org/poise/application) +[![Gem Version](https://img.shields.io/gem/v/poise-application.svg)](https://rubygems.org/gems/poise-application) +[![Cookbook Version](https://img.shields.io/cookbook/v/application.svg)](https://supermarket.chef.io/cookbooks/application) +[![Coverage](https://img.shields.io/codeclimate/coverage/github/poise/application.svg)](https://codeclimate.com/github/poise/application) +[![Gemnasium](https://img.shields.io/gemnasium/poise/application.svg)](https://gemnasium.com/poise/application) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) +A [Chef](https://www.chef.io/) cookbook to deploy applications. -Backwards Compatibility ------------------------ -- Version 4.0.0 dropped support for Chef 10 -- Version 2.0.0 dropped support for the `apps` data bag. +## Getting Started - -Requirements ------------- -The previous dependencies have been moved out to the application-stack-specific cookbooks, and this cookbook has no external dependencies. - - -Resources/Providers -------------------- -The `application` LWRP configures the basic properties of most applications, regardless of the framework or application server they use. These include: - -- SCM information for the deployment, such as the repository URL and branch name; -- deployment destination, including the filesystem path to deploy to; -- any OS packages to install as dependencies; -- optional callback to control the deployment. - -This LWRP uses the `deploy_revision` LWRP to perform the bulk of its tasks, and many concepts and parameters map directly to it. Check the documentation for `deploy_revision` for more information. - -Configuration of framework-specific aspects of the application are performed by invoking a sub-resource; see the appropriate cookbook for more documentation. - -### Actions -- `:deploy`: deploy an application, including any necessary configuration, restarting the associated service if necessary -- `:force_deploy`: same as `:deploy`, but it will send a `:force_deploy` action to the deploy resource, directing it to deploy the application even if the same revision is already deployed - -### Attribute Parameters -- `name`: name attribute. The name of the application you are setting up. This will be used to derive the default value for other attribute -- `packages`: an Array or Hash of packages to be installed before starting the deployment -- `path`: target path of the deployment; it will be created if it does not exist -- `owner`: the user that shall own the target path -- `group`: the group that shall own the target path -- `keep_releases`: count of keep releases -- `strategy`: the underlying LWRP that will be used to perform the deployment. The default is `:deploy_revision`, and it should never be necessary to change it -- `scm_provider`: the provider class to use for the deployment. It defaults to `Chef::Provider::Git`, you can set it to `Chef::Provider::Subversion` to deploy from an SVN repository -- `repository`: the URL of the repository the application should be checked out from -- `revision`: an identifier pointing to the revision that should be checked out -- `deploy_key`: the private key to use to access the repository via SSH, or path to a file containing the key -- `rollback_on_error`: if true, exceptions during a deployment will be caught and a clean rollback to the previous version will be attempted; the exception will then be re-raised. Defaults to true; change it only if you know what you are doing -- `environment`: a Hash of environment variables to set while running migrations -- `purge_before_symlink`: an Array of paths (relative to the checkout) to remove before creating symlinks -- `create_dirs_before_symlink`: an Array of paths (relative to the checkout) pointing to directories to create before creating symlinks -- `symlinks`: a Hash of shared/dir/path => release/dir/path. It determines which files and dirs in the shared directory get symlinked to the current release directory -- `symlink_before_migrate`: similar to symlinks, except that they will be linked before any migration is run -- `migrate`: if `true` then migrations will be run; defaults to false -- `migration_command`: a command to run to migrate the application from the previous to the current state -- `restart_command`: a command to run when restarting the application -- `environment_name`: the name of a framework-specific "environment" (for example the Rails environment). By default it is the same as the Chef environment, unless it is `_default`, in which case it is set to `production` -- `enable_submodules`: whether to enable git submodules in the deploy, passed into the deploy resource. - -### Callback Attributes -You can also set a few attributes on this LWRP that are interpreted as callback to be called at specific points during a deployment. If you pass a block, it will be evaluated within a new context. If you pass a string, it will be interpreted as a path (relative to the release directory) to a file; if it exists, it will be loaded and evaluated as though it were a Chef recipe. - -The following callback attributes are available: - -- `before_deploy`: invoked immediately after initial setup and before the deployment proper is started. This callback will be invoked on every Chef run -- `before_migrate` -- `before_symlink` -- `before_restart` -- `after_restart` - -### Sub-resources -Anything that is not a known attribute will be interpreted as the name of a sub-resource; the resource will be looked up, and any nested attribute will be passed to it. More than one sub-resource can be added to an application; the order is significant, with the latter sub-resources overriding any sub-resource that comes before. - -Sub-resources can set their own values for some attributes; if they do, they will be merged together with the attribute set on the main resource. The attributes that support this behavior are the following: - -- `environment`: environment variables from the application and from sub-resources will be merged together, with later resources overriding values set in the application or previous resources -- `migration_command`: commands from the application and from sub-resources will be concatenated together joined with '&&' and run as a single shell command. The migration will only succeed if all the commands succeed -- `restart_command`: commands from the application and from sub-resources will be evaluated in order -- `symlink_before_migrate`: will be concatenated as a single array -- `callbacks`: sub-resources callbacks will be invoked first, followed by the application callbacks - - -Usage ------ -To use the application cookbook we recommend creating a cookbook named after the application, e.g. `my_app`. In `metadata.rb` you should declare a dependency on this cookbook and any framework cookbook the application may need. For example a Rails application may include: +The application cookbook provides a central framework to deploy applications +using Chef. Generally this will be web applications using things like Rails, +Django, or NodeJS, but the framework makes no specific assumptions. The core +`application` resource provides DSL support and helpers, but the heavy lifting +is all done in specific plugins detailed below. Each deployment starts with +an `application` resource: ```ruby -depends 'application' -depends 'application_ruby' +application '/path/to/deploy' do + owner 'root' + group 'root' + + # ... +end ``` -The default recipe should describe your application using the `application` LWRP; you may also include additional recipes, for example to set up a database, queues, search engines and other components of your application. - -A recipe using this LWRP may look like this: +The `application` resource uses the Poise subresource system for plugins. This +means you configure the steps of the deployment like normal recipe code inside +the `application` resource, with a few special additions: ```ruby -application 'my_app' do - path '/deploy/to/dir' - owner 'app-user' - group 'app-group' +application '/path/to/deploy' do + # Application resource properties. + owner 'root' + group 'root' - repository 'http://git.example.com/my-app.git' - revision 'production' - - # Apply the rails LWRP from application_ruby - rails do - # Rails-specific configuration. See the README in the - # application_ruby cookbook for more information. + # Subresources, like normal recipe code. + package 'ruby' + git '/path/to/deploy' do + repository 'https://github.com/example/myapp.git' end - - # Apply the passenger_apache2 LWRP, also from application_ruby - passenger_apache2 do - # Passenger-specific configuration. + application_rails '/path/to/deploy' do + database 'mysql://dbhost/myapp' end end ``` -You can of course use any code necessary to determine the value of attributes: +When evaluating the recipe inside the `application` resource, it first checks +for `application_#{resource}`, as well as looking for an LWRP of the same name +in any cookbook starting with `application_`. This means that a resource named +`application_foo` can be used as `foo` inside the `application` resource: ```ruby -application 'my_app' do - repository 'http://git.example.com/my-app.git' - revision node.chef_environment == 'production' ? 'production' : 'develop' +application '/path/to/deploy' do + owner 'root' + group 'root' + + rails '/path/to/deploy' do + database 'mysql://dbhost/myapp' + end end ``` -Attributes from the application and from sub-resources are merged together: +Additionally if a resource inside the `application` block doesn't have a name, +it uses the same name as the application resource itself: ```ruby -application 'my_app' do - restart_command 'kill -1 `cat /var/run/one.pid`' - environment 'LC_ALL' => 'en', 'FOO' => 'bar' +application '/path/to/deploy' do + owner 'root' + group 'root' rails do - restart_command 'touch /tmp/something' - environment 'LC_ALL' => 'en_US' - end - - passenger_apache2 do - environment 'FOO' => 'baz' - end -end - -# at the end, you will have: -# -# restart_command #=> kill -1 `cat /var/run/one.pid` && touch /tmp/something -# environment #=> LC_ALL=en_US FOO=baz -``` - -Most databases have the concept of migrations (or you can just use your own): - -```ruby -application 'my_app' do - path '/deploy/to/dir' - owner 'app-user' - group 'app-group' - - repository 'http://git.example.com/my-app.git' - revision 'production' - - php do - migrate true - migration_command 'your-applications-migrate-command' + database 'mysql://dbhost/myapp' end end ``` -This will run `your-applications-migrate-command`, with the current directory set to the directory of the current checkout. +Other than those two special features, the recipe code inside the `application` +resource is processed just like any other recipe. -To use the application cookbook, we recommend creating a role named after the application, e.g. `my_app`. Create a Ruby DSL role in your chef-repo, or create the role directly with knife. +## Available Plugins + +* [`application_git`](https://github.com/poise/application_git) – Deploy + application code from a git repository. +* [`application_ruby`](https://github.com/poise/application_ruby) – Manage Ruby + deployments, such as Rails or Sinatra applications. +* [`application_python`](https://github.com/poise/application_python) – Manage + Python deployments, such as Django or Flask applications. +* [`application_javascript`](https://github.com/poise/application_javascript) – + Manage server-side JavaScript deployments using Node.js or io.js. +* `application_java` – *Coming soon!* +* `application_go` – *Coming soon!* +* `application_erlang` – *Coming soon!* + +## Requirements + +Chef 12 or newer is required. + +## Resources + +### `application` + +The `application` resource has top-level configuration properties for each +deployment and acts as a container for other deployment plugin resources. ```ruby -name 'my_app' -description 'My application deployment' -run_list([ - 'recipe[my_app::default]' -]) +application '/opt/test_sinatra' do + git 'https://github.com/example/my_sinatra_app.git' + bundle_install do + deployment true + end + unicorn do + port 9000 + end +end ``` -License and Authors -------------------- -- Author: Adam Jacob () -- Author: Andrea Campi () -- Author: Joshua Timberman () -- Author: Noah Kantrowitz () -- Author: Seth Chisamore () +#### Actions -```text -Copyright 2009-2013, Opscode, Inc. +* `:deploy` – Deploy the application. *(default)* +* `:start` - Run `:start` on all subresources that support it. +* `:stop` - Run `:stop` on all subresources that support it. +* `:restart` - Run `:restart` on all subresources that support it. +* `:reload` - Run `:reload` on all subresources that support it. + +#### Properties + +* `path` – Path to deploy the application to. *(name attribute)* +* `environment` – Environment variables for all application deployment steps. +* `group` – System group to deploy the application as. +* `owner` – System user to deploy the application as. +* `action_on_update` – Action to run on the application resource when any + subresource is updated. *(default: restart)* +* `action_on_update_immediately` – Run the `action_on_update` notification with + `:immediately`. *(default: false)* + +### `application_cookbook_file`, `application_file`, `application_template` + +The `application_cookbook_file`, `application_file`, and `application_template` +resources extend the core Chef resources to take some application-level +configuration in to account: + +```ruby +application '/opt/myapp' do + template 'myapp.conf' do + source 'myapp.conf.erb' + end +end +``` + +If the resource name is a relative path, it will be expanded relative to the +application path. If an owner or group is declared for the application, those +will be the default user and group for the resource. + +All other actions and properties are the same as the similar resource in core Chef. + +## Examples + +Some test recipes are available as examples for common application frameworks: + +* [Sinatra](https://github.com/poise/application_ruby/blob/master/test/cookbooks/application_ruby_test/recipes/sinatra.rb) +* [Rails](https://github.com/poise/application_ruby/blob/master/test/cookbooks/application_ruby_test/recipes/rails.rb) +* [Flask](https://github.com/poise/application_python/blob/master/test/cookbooks/application_python_test/recipes/flask.rb) +* [Django](https://github.com/poise/application_python/blob/master/test/cookbooks/application_python_test/recipes/django.rb) +* [Express](https://github.com/poise/application_javascript/blob/master/test/cookbooks/application_javascript_test/recipes/express.rb) + +## Upgrading From 4.x + +While the overall design of the revamped application resource is similar to the +4.x version, some changes will need to be made. The `name` property no longer +exists, with the name attribute being used as the path to the deployment. +The `packages` property has been removed as this is more easily handled via +normal recipe code. + +The SCM-related properties like `repository` and `revision` are now handled by +normal plugins. If you were deploying from a private git repository you will +likely want to use the `application_git` cookbook, otherwise just use the +built-in `git` or `svn` resources as per normal. + +The properties related to the `deploy` resource like `strategy` and `symlinks` +have been removed. The `deploy` resource is no longer used so these aren't +relevant. As a side effect of this, you'll likely want to point the upgraded +deployment at a new folder or manually clean the `current` and `shared` folders +from the existing folder. The pseudo-Capistrano layout used by the `deploy` +resource has few benefits in a config-managed world and introduced a lot of +complexity and moving pieces that are no longer required. + +With the removal of the `deploy` resource, the callback properties and commands +are no longer used as well. Subresources no longer use the complex +actions-as-callbacks arrangement as existed before, instead following normal +Chef recipe flow. Individual subresources may need to be tweaked to work with +newer versions of the cookbooks they come from, though most have stayed similar +in overall approach. + +## Database Migrations and Chef + +Several of the web application deployment plugins include optional support to +run database migrations from Chef. For "toy" applications where the app and +database run together on a single machine, this is fine and is a nice time +saver. For anything more complex I highly recommend not running database +migrations from Chef. Some initial operations like creating the database and/or +database user are more reasonable as they tend to be done only once and by their +nature the application does not yet have users so some level of eventual +consistency is more acceptable. With migrations on a production application, I +encourage using Chef and the application cookbooks to handle deploying the code +and writing configuration files, but use something more specific to run the +actual migration task. [Fabric](http://www.fabfile.org/), +[Capistrano](http://capistranorb.com/), and [Rundeck](http://rundeck.org/) are +all good choices for this orchestration tooling. + +Migrations can generally be applied idempotently but they have unique +constraints (pun definitely intended) that make them tricky in a Chef-like, +convergence-based system. First and foremost is that many table alterations +lock the table for updating for at least some period of time. That can mean that +while staging the new code or configuration data can happen within a window, the +migration itself needs to be run in careful lockstep with the rest of the +deployment process (eg. moving things in and out of load balancers). Beyond +that, while most web frameworks have internal idempotence checks for migrations, +running the process on two servers at the same time can have unexpected effects. + +Overall migrations are best thought of as a procedural step rather than a +declaratively modeled piece of the system. + +## Application Signals and Updates + +The `application` resource exposes `start`, `stop`, `restart`, and `reload` +actions which will dispatch to any subresources attached to the application. +This allows for generic application-level restart or reload signals that will +work with any type of deployment. + +Additionally the `action_on_update` property is used to set a default +notification so any subresource that updates will trigger an application +restart or reload. This can be disabled by setting `action_on_update false` if +you want to take manual control of service restarts. + +## Sponsors + +Development sponsored by [Chef Software](https://www.chef.io/), [Symonds & Son](http://symondsandson.com/), and [Orion](https://www.orionlabs.co/). + +The Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/). + +## License + +Copyright 2015, Noah Kantrowitz Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +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/cookbooks/application/files/halite_gem/poise_application.rb b/cookbooks/application/files/halite_gem/poise_application.rb new file mode 100644 index 0000000..13c9825 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application.rb @@ -0,0 +1,25 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseApplication + autoload :AppMixin, 'poise_application/app_mixin' + autoload :Error, 'poise_application/error' + autoload :Resources, 'poise_application/resources' + autoload :ServiceMixin, 'poise_application/service_mixin' + autoload :Utils, 'poise_application/utils' + autoload :VERSION, 'poise_application/version' +end diff --git a/cookbooks/application/files/halite_gem/poise_application/app_file_mixin.rb b/cookbooks/application/files/halite_gem/poise_application/app_file_mixin.rb new file mode 100644 index 0000000..9f4a377 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/app_file_mixin.rb @@ -0,0 +1,61 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/utils' + +require 'poise_application/app_mixin' + + +module PoiseApplication + # A helper mixin for `file`-like resources to make them take application + # resource data. Relative paths are expanded against the application path and + # the app owner/group are the default user/group for the resource. + # + # @api private + # @since 5.1.0 + module AppFileMixin + include Poise::Utils::ResourceProviderMixin + + module Resource + include PoiseApplication::AppMixin + + def initialize(*) + super + # So our lazy default below can work. Not needed on 12.7+. + remove_instance_variable(:@path) if instance_variable_defined?(:@path) + end + + # @!attribute path + # Override the default path to be relative to the app path. + # @return [String] + attribute(:path, kind_of: String, default: lazy { parent ? ::File.expand_path(name, parent.path) : name }) + + # @!attribute group + # Override the default group to be the app group if unspecified. + # @return [String, Integer] + attribute(:group, kind_of: [String, Integer, NilClass], default: lazy { parent && parent.group }) + + # @!attribute user + # Override the default user to be the app owner if unspecified. + # @return [String, Integer] + attribute(:user, kind_of: [String, Integer, NilClass], default: lazy { parent && parent.owner }) + end + + module Provider + include PoiseApplication::AppMixin + end + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/app_mixin.rb b/cookbooks/application/files/halite_gem/poise_application/app_mixin.rb new file mode 100644 index 0000000..7ce3d53 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/app_mixin.rb @@ -0,0 +1,69 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mash' +require 'poise/provider' +require 'poise/resource' +require 'poise/utils' + + +module PoiseApplication + # A helper mixin for application resources and providers. These are things + # intended to be used as subresources of the `application` resource. + # + # @since 5.0.0 + module AppMixin + include Poise::Utils::ResourceProviderMixin + + # A helper mixin for application resources. + module Resource + include Poise::Resource + + # Set the parent type and optional flag. + poise_subresource(:application, true) + + # @!attribute path + # Base path for the application. + # @return [String] + attribute(:path, kind_of: String, name_attribute: true) + + # A delegator for accessing the application state. If no application + # parent is found, the state will be tracked internally within the + # resource. + # + # @return [Hash] + def app_state + if parent + parent.app_state + else + # If there isn't a parent, just track within the resource. + @local_app_state ||= Mash.new + end + end + + # Environment variables stored in the application state. + # + # @return [Hash] + def app_state_environment + app_state[:environment] ||= Mash.new + end + end + + module Provider + include Poise::Provider + end + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/cheftie.rb b/cookbooks/application/files/halite_gem/poise_application/cheftie.rb new file mode 100644 index 0000000..622ba83 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/cheftie.rb @@ -0,0 +1,17 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application/resources' diff --git a/cookbooks/application/files/halite_gem/poise_application/error.rb b/cookbooks/application/files/halite_gem/poise_application/error.rb new file mode 100644 index 0000000..4156956 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/error.rb @@ -0,0 +1,24 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseApplication + # Base exception class for poise-application errors. + # + # @since 5.0.0 + class Error < Exception + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/resources.rb b/cookbooks/application/files/halite_gem/poise_application/resources.rb new file mode 100644 index 0000000..4797a80 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/resources.rb @@ -0,0 +1,29 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application/resources/application' +require 'poise_application/resources/application_cookbook_file' +require 'poise_application/resources/application_file' +require 'poise_application/resources/application_template' + + +module PoiseApplication + # Chef resources and providers for poise-application. + # + # @since 5.0.0 + module Resources + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/resources/application.rb b/cookbooks/application/files/halite_gem/poise_application/resources/application.rb new file mode 100644 index 0000000..e0efbf2 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/resources/application.rb @@ -0,0 +1,259 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/dsl/recipe' # On 12.4+ this will pull in chef/dsl/resources. +require 'chef/resource' +require 'chef/provider' +require 'poise' + + +module PoiseApplication + module Resources + # (see Application::Resource) + # @since 5.0.0 + module Application + # An `application` resource to manage application deployment. + # + # @since 5.0.0 + # @provides application + # @action deploy + # @action start + # @action stop + # @action restart + # @action reload + # @example + # application '/srv/myapp' do + # git '...' + # poise_service 'myapp' do + # command '/srv/myapp/main' + # end + # end + class Resource < Chef::Resource + include Poise(container: true, container_namespace: false) + provides(:application) + actions(:deploy, :start, :stop, :restart, :reload) + + # @!attribute path + # Application base path. + # @return [String] + attribute(:path, kind_of: String, name_attribute: true) + # @!attribute environment + # Environment variables to set for the whole application. + # @return [Hash] + attribute(:environment, kind_of: Hash, default: lazy { Mash.new }) + # @!attribute owner + # System user that will own the application. This can be overriden in + # individual subresources. + # @return [String] + attribute(:owner, kind_of: String) + # @!attribute group + # System group that will own the application. This can be overriden in + # individual subresources. + # @return [String] + attribute(:group, kind_of: String) + # @!attribute action_on_update + # Action to run when any subresource is updated. Defaults to `:restart`. + # @return [String, Symbol, nil, false] + attribute(:action_on_update, kind_of: [Symbol, String, NilClass, FalseClass], default: :restart) + # @!attribute action_on_update_immediately + # Run the {#action_on_update} notification with `:immediately`. + # @return [Boolean] + attribute(:action_on_update_immediately, equal_to: [true, false], default: false) + + # Run the DSL rewire when the resource object is created. + # @api private + def initialize(*args) + super + _rewire_dsl! if node + end + + # Application-specific state values used as a way to communicate between + # subresources. + # + # @return [Mash] + # @example + # if new_resource.parent && new_resource.parent.app_state['gemfile_path'] + def app_state + @app_state ||= Mash.new(environment: environment) + end + + # Override Container#register_subresource to add our action_on_update. + # + # @api private + def register_subresource(resource) + super.tap do |added| + if added && action_on_update + Chef::Log.debug("[#{self}] Registering #{action_on_update_immediately ? 'immediate ' : ''}#{action_on_update} notification from #{resource}") + resource.notifies action_on_update.to_sym, self, (action_on_update_immediately ? :immediately : :delayed) + end + end + end + + private + + # Find all resources that need to be rewired. This is anything with a + # name starting with application_. + # + # @return [Array] + def _rewire_resources + if defined?(Chef::DSL::Resources) + # Chef >= 12.4. + Chef::DSL::Resources.instance_methods + else + # Chef < 12.4 >= 12.0. + Chef::Resource.descendants.map do |klass| + klass.node_map.instance_variable_get(:@map).keys + if klass.dsl_name.include?('::') + # Probably not valid. + # :nocov: + [] + # :nocov: + else + # Needed for things that don't call provides(). + [klass.dsl_name] + end + end.flatten + end.map {|name| name.to_s }.select {|name| name.start_with?('application_') }.uniq + end + + # Find all cookbooks that might contain LWRPs matching our name scheme. + # + # @return [Array] + def _rewire_cookbooks + # Run context might be unset during test setup. + if run_context + run_context.cookbook_collection.keys.select {|cookbook_name| cookbook_name.start_with?('application_') } + else + [] + end + end + + # Build the mapping of new_name => old_name for each resource to rewire. + # + # @return [Hash] + def _rewire_map + application_cookbooks = _rewire_cookbooks + _rewire_resources.inject({}) do |memo, name| + # Grab the resource class to check if it is an LWRP. + klass = Chef::Resource.resource_for_node(name.to_sym, node) + # Find the part to trim. Check for LWRP first, then just application_. + trim = if klass < Chef::Resource::LWRPBase + application_cookbooks.find {|cookbook_name| name.start_with?(cookbook_name) && name != cookbook_name } || 'application' + else + # Non-LWRPs are assumed to have a better name. + 'application' + end + # Map trimmed to untrimmed. + memo[name[trim.length+1..-1]] = name + memo + end + end + + # Build new DSL methods to implement the foo -> application_foo behavior. + # + # @return [void] + def _rewire_dsl! + # Generate stub methods for all the rewiring. + _rewire_map.each do |new_name, old_name| + # This is defined as a singleton method on self so it looks like + # the DSL but is scoped to just this context. + define_singleton_method(new_name) do |name=nil, *args, &block| + # Store the caller to correct the source_line. + created_at = caller[0] + public_send(old_name, name, *args) do + # Set the declared type to be the native name. + self.declared_type = self.class.resource_name + # Fix the source location. For Chef 12.4 we could do this with the + # declared_at parameter on the initial send. + self.source_line = created_at + # Run the original block. + instance_exec(&block) if block + end + end + end + end + end + + # Provider for `application`. + # + # @since 5.0.0 + # @see Resource + # @provides application + class Provider < Chef::Provider + include Poise + provides(:application) + + # `deploy` action for `application`. Creates the application base folder. + # + # @return [void] + def action_deploy + notifying_block do + directory new_resource.path do + owner new_resource.owner + group new_resource.group + mode '755' + end + end + end + + # `start` action for `application`. Proxies to subresources. + # + # @return [void] + def action_start + proxy_action(:start) + end + + # `stop` action for `application`. Proxies to subresources. + # + # @return [void] + def action_stop + proxy_action(:stop) + end + + # `restart` action for `application`. Proxies to subresources. + # + # @return [void] + def action_restart + proxy_action(:restart) + end + + # `reload` action for `application`. Proxies to subresources. + # + # @return [void] + def action_reload + proxy_action(:reload) + end + + private + + # Proxy an action to any subresources that support it. + # + # @param action [Symbol] Action to proxy. + # @return [void] + def proxy_action(action) + Chef::Log.debug("[#{new_resource} Running proxied #{action} action") + new_resource.subresources.each do |r| + begin + r.run_action(action) if r.allowed_actions.include?(action) + rescue Chef::Exceptions::UnsupportedAction + # Don't care, just move on. + end + end + end + + end + end + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/resources/application_cookbook_file.rb b/cookbooks/application/files/halite_gem/poise_application/resources/application_cookbook_file.rb new file mode 100644 index 0000000..bcb6830 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/resources/application_cookbook_file.rb @@ -0,0 +1,54 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application/app_file_mixin' + + +module PoiseApplication + module Resources + # (see ApplicationCookbookFile::Resource) + # @since 5.1.0 + module ApplicationCookbookFile + # An `application_cookbook_file` resource to manage Chef cookbook_files inside and + # Application cookbook deployment. + # + # @provides application_cookbook_file + # @action create + # @action create_if_missing + # @action delete + # @action touch + # @example + # application '/srv/myapp' do + # cookbook_file 'myapp.conf' do + # source 'myapp.conf' + # end + # end + class Resource < Chef::Resource::CookbookFile + include PoiseApplication::AppFileMixin + provides(:application_cookbook_file) + actions(:create, :create_if_missing, :delete, :touch) + subclass_providers! + + def initialize(*args) + super + # For older Chef. + @resource_name = :application_cookbook_file + end + end + + end + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/resources/application_file.rb b/cookbooks/application/files/halite_gem/poise_application/resources/application_file.rb new file mode 100644 index 0000000..a766088 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/resources/application_file.rb @@ -0,0 +1,54 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application/app_file_mixin' + + +module PoiseApplication + module Resources + # (see ApplicationFile::Resource) + # @since 5.1.0 + module ApplicationFile + # An `application_file` resource to manage Chef files inside and + # Application cookbook deployment. + # + # @provides application_file + # @action create + # @action create_if_missing + # @action delete + # @action touch + # @example + # application '/srv/myapp' do + # file 'myapp.conf' do + # source 'myapp.conf.erb' + # end + # end + class Resource < Chef::Resource::File + include PoiseApplication::AppFileMixin + provides(:application_file) + actions(:create, :create_if_missing, :delete, :touch) + subclass_providers! + + def initialize(*args) + super + # For older Chef. + @resource_name = :application_file + end + end + + end + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/resources/application_template.rb b/cookbooks/application/files/halite_gem/poise_application/resources/application_template.rb new file mode 100644 index 0000000..27e7f0b --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/resources/application_template.rb @@ -0,0 +1,54 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application/app_file_mixin' + + +module PoiseApplication + module Resources + # (see ApplicationTemplate::Resource) + # @since 5.1.0 + module ApplicationTemplate + # An `application_template` resource to manage Chef templates inside and + # Application cookbook deployment. + # + # @provides application_template + # @action create + # @action create_if_missing + # @action delete + # @action touch + # @example + # application '/srv/myapp' do + # template 'myapp.conf' do + # source 'myapp.conf.erb' + # end + # end + class Resource < Chef::Resource::Template + include PoiseApplication::AppFileMixin + provides(:application_template) + actions(:create, :create_if_missing, :delete, :touch) + subclass_providers! + + def initialize(*args) + super + # For older Chef. + @resource_name = :application_template + end + end + + end + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/service_mixin.rb b/cookbooks/application/files/halite_gem/poise_application/service_mixin.rb new file mode 100644 index 0000000..176fb29 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/service_mixin.rb @@ -0,0 +1,116 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'chef/provider' +require 'poise/utils' +require 'poise_service/service_mixin' +require 'poise_service/utils' + +require 'poise_application/app_mixin' +require 'poise_application/utils' + + +module PoiseApplication + # Mixin for application services. This is any resource that will be part of + # an application deployment and involves running a persistent service. + # + # @api public + # @since 5.0.0 + # @example + # module MyApp + # class Resource < Chef::Resource + # include Poise + # provides(:my_app) + # include PoiseApplication::ServiceMixin + # end + # + # class Provider < Chef::Provider + # include Poise + # provides(:my_app) + # include PoiseApplication::ServiceMixin + # + # def action_enable + # notifying_block do + # template '/etc/myapp.conf' do + # # ... + # end + # end + # super + # end + # + # def service_options(r) + # super + # r.command('myapp --serve') + # end + # end + # end + module ServiceMixin + include Poise::Utils::ResourceProviderMixin + + # Mixin for application service resources. + # + # @see ServiceMixin + module Resource + include PoiseService::ServiceMixin::Resource + include PoiseApplication::AppMixin::Resource + + module ClassMethods + # @api private + def included(klass) + super + klass.extend(ClassMethods) + klass.class_exec do + attribute(:path, kind_of: String, name_attribute: true) + # Redefines from the PoiseService version so we get a better default. + attribute(:service_name, kind_of: String, default: lazy { PoiseService::Utils.parse_service_name(path) }) + attribute(:user, kind_of: [String, Integer], default: lazy { parent ? parent.owner : 'root' }) + end + end + end + + extend ClassMethods + end + + # Mixin for application service providers. + # + # @see ServiceMixin + module Provider + include PoiseService::ServiceMixin::Provider + include PoiseApplication::AppMixin::Provider + + private + + # Abstract hook to set parameters on {#service_resource} when it is + # created. This is required to set at least `resource.command`. + # + # @api public + # @param resource [Chef::Resource] Resource instance to set parameters on. + # @return [void] + # @example + # def service_options(resource) + # super + # resource.command('myapp --serve') + # end + def service_options(resource) + super + resource.directory(new_resource.path) + resource.user(new_resource.user) + resource.environment.update(new_resource.app_state_environment) if new_resource.parent + end + end + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/utils.rb b/cookbooks/application/files/halite_gem/poise_application/utils.rb new file mode 100644 index 0000000..cc3f874 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/utils.rb @@ -0,0 +1,51 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'etc' + + +module PoiseApplication + # Utility methods for PoiseApplication. + # + # @api public + # @since 5.0.0 + module Utils + # Methods are also available as module-level methods as well as a mixin. + extend self + + # Try to find the primary group name for a given user. + # + # @param user [String, Integer] User to check, if given as an integer this + # is used as a UID, otherwise it is the username. + # @return [String] + # @example + # attribute(:group, kind_of: [String, Integer], default: lazy { PoiseApplication::Utils.primary_group_for(user) }) + def primary_group_for(user) + # Force a reload in case any users were created earlier in the run. + Etc.endpwent + Etc.endgrent + user = if user.is_a?(Integer) + Etc.getpwuid(user) + else + Etc.getpwnam(user.to_s) + end + Etc.getgrgid(user.gid).name + rescue ArgumentError + # One of the get* calls exploded. ¯\_(ツ)_/¯ + user.to_s + end + end +end diff --git a/cookbooks/application/files/halite_gem/poise_application/version.rb b/cookbooks/application/files/halite_gem/poise_application/version.rb new file mode 100644 index 0000000..bb3f482 --- /dev/null +++ b/cookbooks/application/files/halite_gem/poise_application/version.rb @@ -0,0 +1,20 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseApplication + VERSION = '5.1.0' +end diff --git a/cookbooks/application/libraries/default.rb b/cookbooks/application/libraries/default.rb index 7cf6d2a..ebf4f00 100644 --- a/cookbooks/application/libraries/default.rb +++ b/cookbooks/application/libraries/default.rb @@ -1,15 +1,11 @@ # -# Author:: Noah Kantrowitz -# Cookbook Name:: application -# Library:: default -# -# Copyright:: 2011-2012, Opscode, Inc +# Copyright 2015, Noah Kantrowitz # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# 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, @@ -18,172 +14,6 @@ # limitations under the License. # -require "chef/mixin/from_file" - -class Chef - class Resource - # Monkey-Patch the blacklists to prevent infinite recursion in #to_json and similar - # (this is global state,thus the set-union operator to change it idempotently + to not leak) - [ :@application, :@application_provider ].each do |ivar| - FORBIDDEN_IVARS << ivar unless FORBIDDEN_IVARS.include?(ivar) - HIDDEN_IVARS << ivar unless HIDDEN_IVARS.include?(ivar) - end - end -end - -class ApplicationCookbook - - module OptionsCollector - def options - @options ||= {} - end - - def method_missing(method_sym, value=nil, &block) - super - rescue NameError - value ||= block - method_sym = method_sym.to_s.chomp('=').to_sym - options[method_sym] = value if value - options[method_sym] ||= nil - end - end - - module ResourceBase - def self.included(klass) - klass.actions :before_compile, :before_deploy, :before_migrate, :before_symlink, :before_restart, :after_restart - klass.attribute :id, :kind_of => String, :name_attribute => true - klass.attribute :environment, :kind_of => Hash, :default => {} - klass.attribute :purge_before_symlink, :kind_of => Array, :default => [] - klass.attribute :create_dirs_before_symlink, :kind_of => Array, :default => [] - klass.attribute :symlinks, :kind_of => Hash, :default => {} - klass.attribute :symlink_before_migrate, :kind_of => Hash, :default => {} - klass.attribute :migration_command, :kind_of => [String, NilClass], :default => nil - klass.attribute :application - klass.attribute :application_provider - klass.attribute :type - end - - def restart_command(arg=nil, &block) - arg ||= block - raise "Invalid restart command" unless !arg || arg.is_a?(String) || arg.is_a?(Proc) - @restart_command = arg if arg - @restart_command - end - - def method_missing(name, *args) - if application.respond_to? name - application.send(name, *args) - else - super - end - end - - class OptionsBlock - include ApplicationCookbook::OptionsCollector - end - - def options_block(options=nil, &block) - options ||= {} - if block - collector = OptionsBlock.new - collector.instance_eval(&block) - options.update(collector.options) - end - options - end - - def find_matching_role(role, single=true, &block) - return nil if !role - nodes = [] - if node['roles'].include? role - nodes << node - end - if !single || nodes.empty? - search(:node, "role:#{role} AND chef_environment:#{node.chef_environment}") do |n| - nodes << n - end - end - if block - nodes.each do |n| - yield n - end - else - if single - nodes.first - else - nodes - end - end - end - - def find_database_server(role) - dbm = find_matching_role(role) - Chef::Log.warn("No node with role #{role}") if role && !dbm - - if respond_to?(:database) && database.has_key?('host') - database['host'] - elsif dbm && dbm.attribute?('cloud') - dbm['cloud']['local_ipv4'] - elsif dbm - dbm['ipaddress'] - end - end - end - - module ProviderBase - - def self.included(klass) - klass.send(:include, Chef::Mixin::FromFile) - end - - def deploy_provider - @deploy_provider ||= begin - provider = @deploy_resource.provider_for_action(:nothing) - provider.load_current_resource - provider - end - end - - def release_path - deploy_provider.release_path - end - - def shared_path - @deploy_resource.shared_path - end - - def callback(what, callback_code=nil) - Chef::Log.debug("Got callback #{what}: #{callback_code.inspect}") - @collection = Chef::ResourceCollection.new - case callback_code - when Proc - Chef::Log.info "#{@new_resource} running callback #{what}" - safe_recipe_eval(&callback_code) - when String - callback_file = "#{release_path}/#{callback_code}" - unless ::File.exist?(callback_file) - raise RuntimeError, "Can't find your callback file #{callback_file}" - end - run_callback_from_file(callback_file) - when nil - nil - else - raise RuntimeError, "You gave me a callback I don't know what to do with: #{callback_code.inspect}" - end - end - - def run_callback_from_file(callback_file) - if ::File.exist?(callback_file) - Dir.chdir(release_path) do - Chef::Log.info "#{@new_resource} running deploy hook #{callback_file}" - safe_recipe_eval { from_file(callback_file) } - end - end - end - - def safe_recipe_eval(&callback_code) - recipe_eval(&callback_code) - converge if respond_to?(:converge) - end - end -end +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) +require "poise_application/cheftie" diff --git a/cookbooks/application/libraries/matchers.rb b/cookbooks/application/libraries/matchers.rb deleted file mode 100644 index eb94f1e..0000000 --- a/cookbooks/application/libraries/matchers.rb +++ /dev/null @@ -1,11 +0,0 @@ -if defined?(ChefSpec) - - def deploy_application(resource) - ChefSpec::Matchers::ResourceMatcher.new(:application, :deploy, resource) - end - - def force_deploy_application(resource) - ChefSpec::Matchers::ResourceMatcher.new(:application, :force_deploy, resource) - end - -end diff --git a/cookbooks/application/metadata.json b/cookbooks/application/metadata.json index bea510a..a491ee2 100644 --- a/cookbooks/application/metadata.json +++ b/cookbooks/application/metadata.json @@ -1,30 +1 @@ -{ - "name": "application", - "version": "4.1.6", - "description": "Deploys and configures a variety of applications", - "long_description": "Application cookbook\n====================\nThis cookbook is designed to be able to describe and deploy web applications. It provides the basic infrastructure; other cookbooks are required to support specific combinations of frameworks and application servers. The following cookbooks are available at this time:\n\n- [application_java](https://github.com/opscode-cookbooks/application_java) (Java and Tomcat)\n- [application_nginx](https://github.com/opscode-cookbooks/application_nginx) (nginx reverse proxy)\n- [application_php](https://github.com/opscode-cookbooks/application_php) (PHP with `mod_php_apache2`)\n- [application_python](https://github.com/opscode-cookbooks/application_python) (Django with Gunicorn)\n- [application_ruby](https://github.com/opscode-cookbooks/application_ruby) (Rails with Passenger or Unicorn)\n\n\nBackwards Compatibility\n-----------------------\n- Version 4.0.0 dropped support for Chef 10\n- Version 2.0.0 dropped support for the `apps` data bag.\n\n\nRequirements\n------------\nThe previous dependencies have been moved out to the application-stack-specific cookbooks, and this cookbook has no external dependencies.\n\n\nResources/Providers\n-------------------\nThe `application` LWRP configures the basic properties of most applications, regardless of the framework or application server they use. These include:\n\n- SCM information for the deployment, such as the repository URL and branch name;\n- deployment destination, including the filesystem path to deploy to;\n- any OS packages to install as dependencies;\n- optional callback to control the deployment.\n\nThis LWRP uses the `deploy_revision` LWRP to perform the bulk of its tasks, and many concepts and parameters map directly to it. Check the documentation for `deploy_revision` for more information.\n\nConfiguration of framework-specific aspects of the application are performed by invoking a sub-resource; see the appropriate cookbook for more documentation.\n\n### Actions\n- `:deploy`: deploy an application, including any necessary configuration, restarting the associated service if necessary\n- `:force_deploy`: same as `:deploy`, but it will send a `:force_deploy` action to the deploy resource, directing it to deploy the application even if the same revision is already deployed\n\n### Attribute Parameters\n- `name`: name attribute. The name of the application you are setting up. This will be used to derive the default value for other attribute\n- `packages`: an Array or Hash of packages to be installed before starting the deployment\n- `path`: target path of the deployment; it will be created if it does not exist\n- `owner`: the user that shall own the target path\n- `group`: the group that shall own the target path\n- `keep_releases`: count of keep releases\n- `strategy`: the underlying LWRP that will be used to perform the deployment. The default is `:deploy_revision`, and it should never be necessary to change it\n- `scm_provider`: the provider class to use for the deployment. It defaults to `Chef::Provider::Git`, you can set it to `Chef::Provider::Subversion` to deploy from an SVN repository\n- `repository`: the URL of the repository the application should be checked out from\n- `revision`: an identifier pointing to the revision that should be checked out\n- `deploy_key`: the private key to use to access the repository via SSH, or path to a file containing the key\n- `rollback_on_error`: if true, exceptions during a deployment will be caught and a clean rollback to the previous version will be attempted; the exception will then be re-raised. Defaults to true; change it only if you know what you are doing\n- `environment`: a Hash of environment variables to set while running migrations\n- `purge_before_symlink`: an Array of paths (relative to the checkout) to remove before creating symlinks\n- `create_dirs_before_symlink`: an Array of paths (relative to the checkout) pointing to directories to create before creating symlinks\n- `symlinks`: a Hash of shared/dir/path => release/dir/path. It determines which files and dirs in the shared directory get symlinked to the current release directory\n- `symlink_before_migrate`: similar to symlinks, except that they will be linked before any migration is run\n- `migrate`: if `true` then migrations will be run; defaults to false\n- `migration_command`: a command to run to migrate the application from the previous to the current state\n- `restart_command`: a command to run when restarting the application\n- `environment_name`: the name of a framework-specific \"environment\" (for example the Rails environment). By default it is the same as the Chef environment, unless it is `_default`, in which case it is set to `production`\n- `enable_submodules`: whether to enable git submodules in the deploy, passed into the deploy resource.\n\n### Callback Attributes\nYou can also set a few attributes on this LWRP that are interpreted as callback to be called at specific points during a deployment. If you pass a block, it will be evaluated within a new context. If you pass a string, it will be interpreted as a path (relative to the release directory) to a file; if it exists, it will be loaded and evaluated as though it were a Chef recipe.\n\nThe following callback attributes are available:\n\n- `before_deploy`: invoked immediately after initial setup and before the deployment proper is started. This callback will be invoked on every Chef run\n- `before_migrate`\n- `before_symlink`\n- `before_restart`\n- `after_restart`\n\n### Sub-resources\nAnything that is not a known attribute will be interpreted as the name of a sub-resource; the resource will be looked up, and any nested attribute will be passed to it. More than one sub-resource can be added to an application; the order is significant, with the latter sub-resources overriding any sub-resource that comes before.\n\nSub-resources can set their own values for some attributes; if they do, they will be merged together with the attribute set on the main resource. The attributes that support this behavior are the following:\n\n- `environment`: environment variables from the application and from sub-resources will be merged together, with later resources overriding values set in the application or previous resources\n- `migration_command`: commands from the application and from sub-resources will be concatenated together joined with '&&' and run as a single shell command. The migration will only succeed if all the commands succeed\n- `restart_command`: commands from the application and from sub-resources will be evaluated in order\n- `symlink_before_migrate`: will be concatenated as a single array\n- `callbacks`: sub-resources callbacks will be invoked first, followed by the application callbacks\n\n\nUsage\n-----\nTo use the application cookbook we recommend creating a cookbook named after the application, e.g. `my_app`. In `metadata.rb` you should declare a dependency on this cookbook and any framework cookbook the application may need. For example a Rails application may include:\n\n```ruby\ndepends 'application'\ndepends 'application_ruby'\n```\n\nThe default recipe should describe your application using the `application` LWRP; you may also include additional recipes, for example to set up a database, queues, search engines and other components of your application.\n\nA recipe using this LWRP may look like this:\n\n```ruby\napplication 'my_app' do\n path '/deploy/to/dir'\n owner 'app-user'\n group 'app-group'\n\n repository 'http://git.example.com/my-app.git'\n revision 'production'\n\n # Apply the rails LWRP from application_ruby\n rails do\n # Rails-specific configuration. See the README in the\n # application_ruby cookbook for more information.\n end\n\n # Apply the passenger_apache2 LWRP, also from application_ruby\n passenger_apache2 do\n # Passenger-specific configuration.\n end\nend\n```\n\nYou can of course use any code necessary to determine the value of attributes:\n\n```ruby\napplication 'my_app' do\n repository 'http://git.example.com/my-app.git'\n revision node.chef_environment == 'production' ? 'production' : 'develop'\nend\n```\n\nAttributes from the application and from sub-resources are merged together:\n\n```ruby\napplication 'my_app' do\n restart_command 'kill -1 `cat /var/run/one.pid`'\n environment 'LC_ALL' => 'en', 'FOO' => 'bar'\n\n rails do\n restart_command 'touch /tmp/something'\n environment 'LC_ALL' => 'en_US'\n end\n\n passenger_apache2 do\n environment 'FOO' => 'baz'\n end\nend\n\n# at the end, you will have:\n#\n# restart_command #=> kill -1 `cat /var/run/one.pid` && touch /tmp/something\n# environment #=> LC_ALL=en_US FOO=baz\n```\n\nMost databases have the concept of migrations (or you can just use your own):\n\n```ruby\napplication 'my_app' do\n path '/deploy/to/dir'\n owner 'app-user'\n group 'app-group'\n\n repository 'http://git.example.com/my-app.git'\n revision 'production'\n\n php do\n migrate true\n migration_command 'your-applications-migrate-command'\n end\nend\n```\n\nThis will run `your-applications-migrate-command`, with the current directory set to the directory of the current checkout.\n\nTo use the application cookbook, we recommend creating a role named after the application, e.g. `my_app`. Create a Ruby DSL role in your chef-repo, or create the role directly with knife.\n\n```ruby\nname 'my_app'\ndescription 'My application deployment'\nrun_list([\n 'recipe[my_app::default]'\n])\n```\n\nLicense and Authors\n-------------------\n- Author: Adam Jacob ()\n- Author: Andrea Campi ()\n- Author: Joshua Timberman ()\n- Author: Noah Kantrowitz ()\n- Author: Seth Chisamore ()\n\n```text\nCopyright 2009-2013, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n", - "maintainer": "Opscode, Inc.", - "maintainer_email": "cookbooks@opscode.com", - "license": "Apache 2.0", - "platforms": { - }, - "dependencies": { - }, - "recommendations": { - }, - "suggestions": { - }, - "conflicting": { - }, - "providing": { - }, - "replacing": { - }, - "attributes": { - }, - "groupings": { - }, - "recipes": { - "application": "Empty placeholder recipe, use the LWRPs, see README.md." - } -} \ No newline at end of file +{"name":"application","version":"5.1.0","description":"A Chef cookbook for deploying application code.","long_description":"# Application cookbook\n\n[![Build Status](https://img.shields.io/travis/poise/application.svg)](https://travis-ci.org/poise/application)\n[![Gem Version](https://img.shields.io/gem/v/poise-application.svg)](https://rubygems.org/gems/poise-application)\n[![Cookbook Version](https://img.shields.io/cookbook/v/application.svg)](https://supermarket.chef.io/cookbooks/application)\n[![Coverage](https://img.shields.io/codeclimate/coverage/github/poise/application.svg)](https://codeclimate.com/github/poise/application)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/application.svg)](https://gemnasium.com/poise/application)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nA [Chef](https://www.chef.io/) cookbook to deploy applications.\n\n## Getting Started\n\nThe application cookbook provides a central framework to deploy applications\nusing Chef. Generally this will be web applications using things like Rails,\nDjango, or NodeJS, but the framework makes no specific assumptions. The core\n`application` resource provides DSL support and helpers, but the heavy lifting\nis all done in specific plugins detailed below. Each deployment starts with\nan `application` resource:\n\n```ruby\napplication '/path/to/deploy' do\n owner 'root'\n group 'root'\n\n # ...\nend\n```\n\nThe `application` resource uses the Poise subresource system for plugins. This\nmeans you configure the steps of the deployment like normal recipe code inside\nthe `application` resource, with a few special additions:\n\n```ruby\napplication '/path/to/deploy' do\n # Application resource properties.\n owner 'root'\n group 'root'\n\n # Subresources, like normal recipe code.\n package 'ruby'\n git '/path/to/deploy' do\n repository 'https://github.com/example/myapp.git'\n end\n application_rails '/path/to/deploy' do\n database 'mysql://dbhost/myapp'\n end\nend\n```\n\nWhen evaluating the recipe inside the `application` resource, it first checks\nfor `application_#{resource}`, as well as looking for an LWRP of the same name\nin any cookbook starting with `application_`. This means that a resource named\n`application_foo` can be used as `foo` inside the `application` resource:\n\n```ruby\napplication '/path/to/deploy' do\n owner 'root'\n group 'root'\n\n rails '/path/to/deploy' do\n database 'mysql://dbhost/myapp'\n end\nend\n```\n\nAdditionally if a resource inside the `application` block doesn't have a name,\nit uses the same name as the application resource itself:\n\n```ruby\napplication '/path/to/deploy' do\n owner 'root'\n group 'root'\n\n rails do\n database 'mysql://dbhost/myapp'\n end\nend\n```\n\nOther than those two special features, the recipe code inside the `application`\nresource is processed just like any other recipe.\n\n## Available Plugins\n\n* [`application_git`](https://github.com/poise/application_git) – Deploy\n application code from a git repository.\n* [`application_ruby`](https://github.com/poise/application_ruby) – Manage Ruby\n deployments, such as Rails or Sinatra applications.\n* [`application_python`](https://github.com/poise/application_python) – Manage\n Python deployments, such as Django or Flask applications.\n* [`application_javascript`](https://github.com/poise/application_javascript) –\n Manage server-side JavaScript deployments using Node.js or io.js.\n* `application_java` – *Coming soon!*\n* `application_go` – *Coming soon!*\n* `application_erlang` – *Coming soon!*\n\n## Requirements\n\nChef 12 or newer is required.\n\n## Resources\n\n### `application`\n\nThe `application` resource has top-level configuration properties for each\ndeployment and acts as a container for other deployment plugin resources.\n\n```ruby\napplication '/opt/test_sinatra' do\n git 'https://github.com/example/my_sinatra_app.git'\n bundle_install do\n deployment true\n end\n unicorn do\n port 9000\n end\nend\n```\n\n#### Actions\n\n* `:deploy` – Deploy the application. *(default)*\n* `:start` - Run `:start` on all subresources that support it.\n* `:stop` - Run `:stop` on all subresources that support it.\n* `:restart` - Run `:restart` on all subresources that support it.\n* `:reload` - Run `:reload` on all subresources that support it.\n\n#### Properties\n\n* `path` – Path to deploy the application to. *(name attribute)*\n* `environment` – Environment variables for all application deployment steps.\n* `group` – System group to deploy the application as.\n* `owner` – System user to deploy the application as.\n* `action_on_update` – Action to run on the application resource when any\n subresource is updated. *(default: restart)*\n* `action_on_update_immediately` – Run the `action_on_update` notification with\n `:immediately`. *(default: false)*\n\n### `application_cookbook_file`, `application_file`, `application_template`\n\nThe `application_cookbook_file`, `application_file`, and `application_template`\nresources extend the core Chef resources to take some application-level\nconfiguration in to account:\n\n```ruby\napplication '/opt/myapp' do\n template 'myapp.conf' do\n source 'myapp.conf.erb'\n end\nend\n```\n\nIf the resource name is a relative path, it will be expanded relative to the\napplication path. If an owner or group is declared for the application, those\nwill be the default user and group for the resource.\n\nAll other actions and properties are the same as the similar resource in core Chef.\n\n## Examples\n\nSome test recipes are available as examples for common application frameworks:\n\n* [Sinatra](https://github.com/poise/application_ruby/blob/master/test/cookbooks/application_ruby_test/recipes/sinatra.rb)\n* [Rails](https://github.com/poise/application_ruby/blob/master/test/cookbooks/application_ruby_test/recipes/rails.rb)\n* [Flask](https://github.com/poise/application_python/blob/master/test/cookbooks/application_python_test/recipes/flask.rb)\n* [Django](https://github.com/poise/application_python/blob/master/test/cookbooks/application_python_test/recipes/django.rb)\n* [Express](https://github.com/poise/application_javascript/blob/master/test/cookbooks/application_javascript_test/recipes/express.rb)\n\n## Upgrading From 4.x\n\nWhile the overall design of the revamped application resource is similar to the\n4.x version, some changes will need to be made. The `name` property no longer\nexists, with the name attribute being used as the path to the deployment.\nThe `packages` property has been removed as this is more easily handled via\nnormal recipe code.\n\nThe SCM-related properties like `repository` and `revision` are now handled by\nnormal plugins. If you were deploying from a private git repository you will\nlikely want to use the `application_git` cookbook, otherwise just use the\nbuilt-in `git` or `svn` resources as per normal.\n\nThe properties related to the `deploy` resource like `strategy` and `symlinks`\nhave been removed. The `deploy` resource is no longer used so these aren't\nrelevant. As a side effect of this, you'll likely want to point the upgraded\ndeployment at a new folder or manually clean the `current` and `shared` folders\nfrom the existing folder. The pseudo-Capistrano layout used by the `deploy`\nresource has few benefits in a config-managed world and introduced a lot of\ncomplexity and moving pieces that are no longer required.\n\nWith the removal of the `deploy` resource, the callback properties and commands\nare no longer used as well. Subresources no longer use the complex\nactions-as-callbacks arrangement as existed before, instead following normal\nChef recipe flow. Individual subresources may need to be tweaked to work with\nnewer versions of the cookbooks they come from, though most have stayed similar\nin overall approach.\n\n## Database Migrations and Chef\n\nSeveral of the web application deployment plugins include optional support to\nrun database migrations from Chef. For \"toy\" applications where the app and\ndatabase run together on a single machine, this is fine and is a nice time\nsaver. For anything more complex I highly recommend not running database\nmigrations from Chef. Some initial operations like creating the database and/or\ndatabase user are more reasonable as they tend to be done only once and by their\nnature the application does not yet have users so some level of eventual\nconsistency is more acceptable. With migrations on a production application, I\nencourage using Chef and the application cookbooks to handle deploying the code\nand writing configuration files, but use something more specific to run the\nactual migration task. [Fabric](http://www.fabfile.org/),\n[Capistrano](http://capistranorb.com/), and [Rundeck](http://rundeck.org/) are\nall good choices for this orchestration tooling.\n\nMigrations can generally be applied idempotently but they have unique\nconstraints (pun definitely intended) that make them tricky in a Chef-like,\nconvergence-based system. First and foremost is that many table alterations\nlock the table for updating for at least some period of time. That can mean that\nwhile staging the new code or configuration data can happen within a window, the\nmigration itself needs to be run in careful lockstep with the rest of the\ndeployment process (eg. moving things in and out of load balancers). Beyond\nthat, while most web frameworks have internal idempotence checks for migrations,\nrunning the process on two servers at the same time can have unexpected effects.\n\nOverall migrations are best thought of as a procedural step rather than a\ndeclaratively modeled piece of the system.\n\n## Application Signals and Updates\n\nThe `application` resource exposes `start`, `stop`, `restart`, and `reload`\nactions which will dispatch to any subresources attached to the application.\nThis allows for generic application-level restart or reload signals that will\nwork with any type of deployment.\n\nAdditionally the `action_on_update` property is used to set a default\nnotification so any subresource that updates will trigger an application\nrestart or reload. This can be disabled by setting `action_on_update false` if\nyou want to take manual control of service restarts.\n\n## Sponsors\n\nDevelopment sponsored by [Chef Software](https://www.chef.io/), [Symonds & Son](http://symondsandson.com/), and [Orion](https://www.orionlabs.co/).\n\nThe Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/).\n\n## License\n\nCopyright 2015, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainer":"Noah Kantrowitz","maintainer_email":"noah@coderanger.net","license":"Apache 2.0","platforms":{},"dependencies":{"poise":"~> 2.4","poise-service":"~> 1.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{}} \ No newline at end of file diff --git a/cookbooks/application/providers/default.rb b/cookbooks/application/providers/default.rb deleted file mode 100644 index f9a0a1f..0000000 --- a/cookbooks/application/providers/default.rb +++ /dev/null @@ -1,191 +0,0 @@ -# -# Author:: Noah Kantrowitz -# Cookbook Name:: application -# Provider:: default -# -# Copyright:: 2011-2012, Opscode, Inc -# -# 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. -# - -include ApplicationCookbook::ProviderBase - -action :deploy do - - before_compile - - before_deploy - - run_deploy - -end - -action :force_deploy do - - before_compile - - before_deploy - - run_deploy(true) - -end - -action :restart do - - before_compile - - run_actions_with_context(:before_restart, @run_context) - - run_restart - - run_actions_with_context(:after_restart, @run_context) - - @new_resource.updated_by_last_action(true) - -end - -protected - -def before_compile - new_resource.application_provider self - new_resource.sub_resources.each do |resource| - resource.application_provider self - resource.run_action :before_compile - end -end - -def before_deploy - new_resource.packages.each do |pkg,ver| - package pkg do - action :install - version ver if ver && ver.length > 0 - end - end - - directory new_resource.path do - owner new_resource.owner - group new_resource.group - mode '0755' - recursive true - end - - directory "#{new_resource.path}/shared" do - owner new_resource.owner - group new_resource.group - mode '0755' - recursive true - end - - if new_resource.deploy_key - - if ::File.exists?(new_resource.deploy_key) - deploy_key = open(new_resource.deploy_key, &:read) - else - deploy_key = new_resource.deploy_key - end - - file "#{new_resource.path}/id_deploy" do - content deploy_key - owner new_resource.owner - group new_resource.group - mode '0600' - end - - template "#{new_resource.path}/deploy-ssh-wrapper" do - source "deploy-ssh-wrapper.erb" - cookbook "application" - owner new_resource.owner - group new_resource.group - mode "0755" - variables :id => new_resource.name, :deploy_to => new_resource.path, :strict_ssh => new_resource.strict_ssh - end - end - - ruby_block "#{new_resource.name} before_deploy" do - block do - new_resource.sub_resources.each do |resource| - resource.run_action :before_deploy - end - callback(:before_deploy, new_resource.before_deploy) - end - end -end - -def run_deploy(force = false) - # Alias to a variable so I can use in sub-resources - new_resource = @new_resource - # Also alias to variable so it can be used in sub-resources - app_provider = self - - @deploy_resource = send(new_resource.strategy.to_sym, new_resource.name) do - action force ? :force_deploy : :deploy - scm_provider new_resource.scm_provider - revision new_resource.revision - repository new_resource.repository - enable_submodules new_resource.enable_submodules - user new_resource.owner - group new_resource.group - keep_releases new_resource.keep_releases - deploy_to new_resource.path - ssh_wrapper "#{new_resource.path}/deploy-ssh-wrapper" if new_resource.deploy_key - shallow_clone new_resource.shallow_clone - rollback_on_error new_resource.rollback_on_error - all_environments = ([new_resource.environment]+new_resource.sub_resources.map{|res| res.environment}).inject({}){|acc, val| acc.merge(val)} - environment all_environments - migrate new_resource.migrate - all_migration_commands = ([new_resource.migration_command]+new_resource.sub_resources.map{|res| res.migration_command}).select{|cmd| cmd && !cmd.empty?} - migration_command all_migration_commands.join(' && ') - restart_command do - ([new_resource]+new_resource.sub_resources).each do |res| - cmd = res.restart_command - if cmd.is_a? Proc - app_provider.deploy_provider.instance_eval(&cmd) # @see libraries/default.rb - elsif cmd && !cmd.empty? - execute cmd do - user new_resource.owner - group new_resource.group - environment all_environments - end - end - end - end - purge_before_symlink (new_resource.purge_before_symlink + new_resource.sub_resources.map(&:purge_before_symlink)).flatten - create_dirs_before_symlink (new_resource.create_dirs_before_symlink + new_resource.sub_resources.map(&:create_dirs_before_symlink)).flatten - all_symlinks = [new_resource.symlinks]+new_resource.sub_resources.map{|res| res.symlinks} - symlinks all_symlinks.inject({}){|acc, val| acc.merge(val)} - all_symlinks_before_migrate = [new_resource.symlink_before_migrate]+new_resource.sub_resources.map{|res| res.symlink_before_migrate} - symlink_before_migrate all_symlinks_before_migrate.inject({}){|acc, val| acc.merge(val)} - before_migrate do - app_provider.send(:run_actions_with_context, :before_migrate, @run_context) - end - before_symlink do - app_provider.send(:run_actions_with_context, :before_symlink, @run_context) - end - before_restart do - app_provider.send(:run_actions_with_context, :before_restart, @run_context) - end - after_restart do - app_provider.send(:run_actions_with_context, :after_restart, @run_context) - end - end -end - -def run_actions_with_context(action, context) - new_resource.sub_resources.each do |resource| - saved_run_context = resource.instance_variable_get :@run_context - resource.instance_variable_set :@run_context, context - resource.run_action action - resource.instance_variable_set :@run_context, saved_run_context - end - callback(action, new_resource.send(action)) -end diff --git a/cookbooks/application/resources/default.rb b/cookbooks/application/resources/default.rb deleted file mode 100644 index ad19c50..0000000 --- a/cookbooks/application/resources/default.rb +++ /dev/null @@ -1,178 +0,0 @@ -# -# Author:: Noah Kantrowitz -# Cookbook Name:: application -# Resource:: default -# -# Copyright:: 2011-2012, Opscode, Inc -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'weakref' - -include Chef::DSL::Recipe - -def initialize(*args) - super - @action = :deploy - @sub_resources = [] -end - -actions :deploy, :force_deploy - -attribute :name, :kind_of => String, :name_attribute => true -attribute :environment_name, :kind_of => String, :default => (node.chef_environment =~ /_default/ ? "production" : node.chef_environment) -attribute :path, :kind_of => String -attribute :owner, :kind_of => String -attribute :group, :kind_of => String -attribute :keep_releases, :kind_of => Integer, :default => 5 -attribute :strategy, :kind_of => [String, Symbol], :default => :deploy_revision -attribute :scm_provider, :kind_of => [Class, String, Symbol] -attribute :revision, :kind_of => String -attribute :repository, :kind_of => String -attribute :enable_submodules, :kind_of => [TrueClass, FalseClass], :default => false -attribute :environment, :kind_of => Hash, :default => {} -attribute :deploy_key, :kind_of => [String, NilClass], :default => nil -attribute :strict_ssh, :kind_of => [TrueClass, FalseClass], :default => false -attribute :shallow_clone, :kind_of => [TrueClass, FalseClass], :default => false -attribute :force, :kind_of => [TrueClass, FalseClass], :default => false -attribute :rollback_on_error, :kind_of => [TrueClass, FalseClass], :default => true -attribute :purge_before_symlink, :kind_of => Array, :default => [] -attribute :create_dirs_before_symlink, :kind_of => Array, :default => [] -attribute :symlinks, :kind_of => Hash, :default => {} -attribute :symlink_before_migrate, :kind_of => Hash, :default => {} -attribute :migrate, :kind_of => [TrueClass, FalseClass], :default => false -attribute :migration_command, :kind_of => [String, NilClass], :default => nil -attribute :packages, :kind_of => [Array, Hash], :default => [] -attribute :application_provider -attr_reader :sub_resources - -def restart_command(arg=nil, &block) - arg ||= block - set_or_return(:restart_command, arg, :kind_of => [Proc, String]) -end - -# Callback fires before deploy is started. -def before_deploy(arg=nil, &block) - arg ||= block - set_or_return(:before_deploy, arg, :kind_of => [Proc, String]) -end - -# Callback fires before migration is run. -def before_migrate(arg=nil, &block) - arg ||= block - set_or_return(:before_migrate, arg, :kind_of => [Proc, String]) -end - -# Callback fires before symlinking -def before_symlink(arg=nil, &block) - arg ||= block - set_or_return(:before_symlink, arg, :kind_of => [Proc, String]) -end - -# Callback fires before restart -def before_restart(arg=nil, &block) - arg ||= block - set_or_return(:before_restart, arg, :kind_of => [Proc, String]) -end - -# Callback fires after restart -def after_restart(arg=nil, &block) - arg ||= block - set_or_return(:after_restart, arg, :kind_of => [Proc, String]) -end - -def release_path - application_provider.release_path -end - -def shared_path - application_provider.shared_path -end - -def method_missing(name, *args, &block) - # Build the set of names to check for a valid resource - lookup_path = ["application_#{name}"] - run_context.cookbook_collection.each do |cookbook_name, cookbook_ver| - if cookbook_name.start_with?("application_") - lookup_path << "#{cookbook_name}_#{name}" - end - end - lookup_path << name - resource = nil - # Try to find our resource - lookup_path.each do |resource_name| - begin - Chef::Log.debug "Trying to load application resource #{resource_name} for #{name}" - resource = super(resource_name.to_sym, self.name, &block) - break - rescue NameError => e - # Works on any MRI ruby - if e.name == resource_name.to_sym || e.inspect =~ /\b#{resource_name}\b/ - next - else - raise e - end - end - end - raise NameError, "No resource found for #{name}. Tried #{lookup_path.join(', ')}" unless resource - # Enforce action :nothing in case people forget - resource.action :nothing - # Make this a weakref to prevent a cycle between the application resource and the sub resources - resource.application WeakRef.new(self) - resource.type name - @sub_resources << resource - resource -end - -def do_i_respond_to?(*args) - name = args.first.to_s - # Build the set of names to check for a valid resource - lookup_path = ["application_#{name}"] - run_context.cookbook_collection.each do |cookbook_name, cookbook_ver| - if cookbook_name.start_with?("application_") - lookup_path << "#{cookbook_name}_#{name}" - end - end - lookup_path << name - found = false - # Try to find our resource - lookup_path.each do |resource_name| - begin - Chef::Log.debug "Looking for application resource #{resource_name} for #{name}" - Chef::Resource.resource_for_node(resource_name.to_sym, node) - found = true - break - rescue NameError => e - # Keep calm and carry on - end - end - found -end - -# If we are using a current version of ruby, use respond_to_missing? -# instead of respond_to? so we provide proper behavior -if(Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('1.9.1')) - def respond_to_missing?(*args) - super || do_i_respond_to?(*args) - end -else - def respond_to?(*args) - super || do_i_respond_to?(*args) - end -end - -def to_ary - nil -end -alias :to_a :to_ary diff --git a/cookbooks/application/templates/default/deploy-ssh-wrapper.erb b/cookbooks/application/templates/default/deploy-ssh-wrapper.erb deleted file mode 100644 index fcf0c81..0000000 --- a/cookbooks/application/templates/default/deploy-ssh-wrapper.erb +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# -# Deploy SSH Wrapper -# App: <%= @id %> -# -# Rendered by Chef - local changes will be replaced - -/usr/bin/env ssh <% unless @strict_ssh %>-o "StrictHostKeyChecking=no" <% end %>-i "<%= @deploy_to %>/id_deploy" $@ diff --git a/cookbooks/application_git/CHANGELOG.md b/cookbooks/application_git/CHANGELOG.md new file mode 100644 index 0000000..2cba57d --- /dev/null +++ b/cookbooks/application_git/CHANGELOG.md @@ -0,0 +1,10 @@ +# Application_Git Changelog + +## v1.1.0 + +* [#2](https://github.com/poise/application_git/issues/2) – Inherit user and group values from the parent `application` resource. +* [#3](https://github.com/poise/application_git/issues/3) – Fix usage with users created during the current Chef run. + +## v1.0.0 + +* Initial release. diff --git a/cookbooks/application_git/README.md b/cookbooks/application_git/README.md new file mode 100644 index 0000000..932fd3a --- /dev/null +++ b/cookbooks/application_git/README.md @@ -0,0 +1,108 @@ +# Application_Git Cookbook + +[![Build Status](https://img.shields.io/travis/poise/application_git.svg)](https://travis-ci.org/poise/application_git) +[![Gem Version](https://img.shields.io/gem/v/poise-application-git.svg)](https://rubygems.org/gems/poise-application-git) +[![Cookbook Version](https://img.shields.io/cookbook/v/application_git.svg)](https://supermarket.chef.io/cookbooks/application_git) +[![Coverage](https://img.shields.io/codecov/c/github/poise/application_git.svg)](https://codecov.io/github/poise/application_git) +[![Gemnasium](https://img.shields.io/gemnasium/poise/application_git.svg)](https://gemnasium.com/poise/application_git) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +A [Chef](https://www.chef.io/) cookbook to handle deploying code from git when +using the [application cookbook](https://github.com/poise/application). + +## Quick Start + +To deploy from a private GitHub repository: + +```ruby +application '/srv/myapp' do + git 'git@github.com:example/myapp.git' do + deploy_key chef_vault_item('deploy_keys', 'myapp')['key'] + end +end +``` + +## Requirements + +Chef 12 or newer is required. + +## Resources + +### `application_git` + +The `application_git` resource deploys code from git. It extends the core `git` +resource to support deploy keys and disabling strict host key verification. + +```ruby +application '/srv/myapp' do + git 'git@github.com:example/myapp.git' +end +``` + +#### Actions + +All actions work the same as the core `git` resource. + +* `:sync` – Clone and checkout the requested revision *(default)* +* `:checkout` – Checkout the request revision. If the repository isn't already + cloned, this action does nothing. +* `:export` – Export the repository without the `.git` folder. + +#### Properties + +All properties from the core `git` resource work the same way with the following +additions: + +* `deploy_key` – SSH key to use with git. Can be specified either as a path to + key file already created or as a string value containing the key directly. +* `strict_ssh` – Enable strict SSH host key checking. *(default: false)* + +### DSL Usage + +The `application_git` resource can be used directly as a replacement for the +core `git` resource: + +```ruby +application_git '/srv/myapp' do + repository 'git@github.com:example/myapp.git' + deploy_key chef_vault_item('deploy_keys', 'myapp')['key'] +end +``` + +Within the `application` resource, a simplified DSL is available. As with other +`application` plugins, the default name of the resource if unspecified is the +application path. The following two examples are equivalent: + +```ruby +application '/srv/myapp' do + git do + repository 'git@github.com:example/myapp.git' + end +end + +application '/srv/myapp' do + git 'git@github.com:example/myapp.git' +end +``` + +## Sponsors + +Development sponsored by [Chef Software](https://www.chef.io/), [Symonds & Son](http://symondsandson.com/), and [Orion](https://www.orionlabs.co/). + +The Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/). + +## License + +Copyright 2015-2016, Noah Kantrowitz + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cookbooks/application_git/files/halite_gem/poise-application-git.rb b/cookbooks/application_git/files/halite_gem/poise-application-git.rb new file mode 100644 index 0000000..bc97c9f --- /dev/null +++ b/cookbooks/application_git/files/halite_gem/poise-application-git.rb @@ -0,0 +1,17 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application_git' diff --git a/cookbooks/application_git/files/halite_gem/poise_application_git.rb b/cookbooks/application_git/files/halite_gem/poise_application_git.rb new file mode 100644 index 0000000..56cb8bb --- /dev/null +++ b/cookbooks/application_git/files/halite_gem/poise_application_git.rb @@ -0,0 +1,21 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application_git/resource' + + +module PoiseApplicationGit +end diff --git a/cookbooks/application_git/files/halite_gem/poise_application_git/cheftie.rb b/cookbooks/application_git/files/halite_gem/poise_application_git/cheftie.rb new file mode 100644 index 0000000..6fcd9c3 --- /dev/null +++ b/cookbooks/application_git/files/halite_gem/poise_application_git/cheftie.rb @@ -0,0 +1,17 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application_git/resource' diff --git a/cookbooks/application_git/files/halite_gem/poise_application_git/resource.rb b/cookbooks/application_git/files/halite_gem/poise_application_git/resource.rb new file mode 100644 index 0000000..ffcf3c2 --- /dev/null +++ b/cookbooks/application_git/files/halite_gem/poise_application_git/resource.rb @@ -0,0 +1,204 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'zlib' + +require 'chef/provider' +require 'chef/resource' +require 'poise_application/app_mixin' +require 'poise_application/resources/application' + +require 'poise_application_git/safe_string' + + +module PoiseApplicationGit + # An `application_git` resource to clone application code from git. + # + # @since 1.0.0 + # @provides application_git + # @action sync + # @action checkout + # @action export + # @example + # application '/srv/myapp' do + # git 'git@github.com:example/myapp.git' do + # deploy_key data_bag_item('deploy_keys', 'myapp')['key'] + # end + # end + class Resource < Chef::Resource::Git + include PoiseApplication::AppMixin + provides(:application_git) + + # @api private + def initialize(*args) + super + # Because the superclass declares this, we have to as well. Should be + # removable at some point when Chef makes everything use the provider + # resolver system instead. + @resource_name = :application_git + @provider = PoiseApplicationGit::Provider + # Clear defaults in older versions of Chef. + remove_instance_variable(:@group) if instance_variable_defined?(:@group) + remove_instance_variable(:@user) if instance_variable_defined?(:@user) + end + + # @!attribute group + # Group to run git as. Defaults to the application group. + # @return [String, Integer, nil, false] + attribute(:group, kind_of: [String, Integer, NilClass, FalseClass], default: lazy { parent && parent.group }) + # @!attribute strict_ssh + # Enable strict SSH host key checking. Defaults to false. + # @return [Boolean] + attribute(:strict_ssh, equal_to: [true, false], default: false) + # @!attribute user + # User to run git as. Defaults to the application owner. + # @return [String, Integer, nil, false] + attribute(:user, kind_of: [String, Integer, NilClass, FalseClass], default: lazy { parent && parent.owner }) + + # @api private + def after_created + # Allow using the repository as the name in an application block. + if parent && !repository + destination(parent.path) + repository(name) + end + end + + # @!attribute deploy_key + # SSH deploy key as either a string value or a path to a key file. + # @return [String] + def deploy_key(val=nil) + # Use a SafeString for literal deploy keys so they aren't shown. + val = SafeString.new(val) if val && !deploy_key_is_local?(val) + set_or_return(:deploy_key, val, kind_of: String) + end + + # Default SSH wrapper path. + # + # @api private + # @return [String] + def ssh_wrapper_path + @ssh_wrapper_path ||= ::File.expand_path("~#{user}/.ssh/ssh_wrapper_#{Zlib.crc32(name)}") + end + + # Guess if the deploy key is a local path or literal value. + # + # @api private + # @param key [String, nil] Key value to check. Defaults to self.key. + # @return [Boolean] + def deploy_key_is_local?(key=nil) + key ||= deploy_key + key && key[0] == '/' + end + + # Path to deploy key. + # + # @api private + # @return [String] + def deploy_key_path + @deploy_key_path ||= if deploy_key_is_local? + deploy_key + else + ::File.expand_path("~#{user}/.ssh/id_deploy_#{Zlib.crc32(name)}") + end + end + end + + # Provider for `application_git`. + # + # @since 1.0.0 + # @see Resource + # @provides application_git + class Provider < Chef::Provider::Git + include PoiseApplication::AppMixin + provides(:application_git) + + # @api private + def initialize(*args) + super + # Set the SSH wrapper path in a late-binding kind of way. This better + # supports situations where the user doesn't exist until Chef converges. + new_resource.ssh_wrapper(new_resource.ssh_wrapper_path) if new_resource.deploy_key + end + + # @api private + def whyrun_supported? + false # Just not dealing with this right now + end + + # Hack our special login in before load_current_resource runs because that + # needs access to the git remote. + # + # @api private + def load_current_resource + include_recipe('git') + notifying_block do + create_dotssh + write_deploy_key + write_ssh_wrapper + end if new_resource.deploy_key + super + end + + private + + # Create a .ssh folder for the user. + # + # @return [void] + def create_dotssh + directory ::File.expand_path("~#{new_resource.user}/.ssh") do + owner new_resource.user + group new_resource.group + mode '755' + end + end + + # Copy the deploy key to a file if needed. + # + # @return [void] + def write_deploy_key + # Check if we have a local path or some actual content + return if new_resource.deploy_key_is_local? + file new_resource.deploy_key_path do + owner new_resource.user + group new_resource.group + mode '600' + content new_resource.deploy_key + sensitive true + end + end + + # Create the SSH wrapper script. + # + # @return [void] + def write_ssh_wrapper + # Write out the GIT_SSH script, it should already be enabled above + file new_resource.ssh_wrapper_path do + owner new_resource.user + group new_resource.group + mode '700' + content %Q{#!/bin/sh\n/usr/bin/env ssh #{'-o "StrictHostKeyChecking=no" ' unless new_resource.strict_ssh}-i "#{new_resource.deploy_key_path}" $@\n} + end + end + + # Patch back in the `#git` from the git provider. This otherwise conflicts + # with the `#git` defined by the DSL, which gets included in such a way + # that the DSL takes priority. + def git(*args, &block) + Chef::Provider::Git.instance_method(:git).bind(self).call(*args, &block) + end + end +end diff --git a/cookbooks/application_git/files/halite_gem/poise_application_git/safe_string.rb b/cookbooks/application_git/files/halite_gem/poise_application_git/safe_string.rb new file mode 100644 index 0000000..c975c49 --- /dev/null +++ b/cookbooks/application_git/files/halite_gem/poise_application_git/safe_string.rb @@ -0,0 +1,25 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseApplicationGit + # A string that won't be shown in Chef error output + class SafeString < String + def to_text + '"suppressed sensitive value"' + end + end +end diff --git a/cookbooks/application_git/files/halite_gem/poise_application_git/version.rb b/cookbooks/application_git/files/halite_gem/poise_application_git/version.rb new file mode 100644 index 0000000..0a67268 --- /dev/null +++ b/cookbooks/application_git/files/halite_gem/poise_application_git/version.rb @@ -0,0 +1,20 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseApplicationGit + VERSION = '1.1.0' +end diff --git a/cookbooks/application_git/libraries/default.rb b/cookbooks/application_git/libraries/default.rb new file mode 100644 index 0000000..868f2a0 --- /dev/null +++ b/cookbooks/application_git/libraries/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) +require "poise_application_git/cheftie" diff --git a/cookbooks/application_git/metadata.json b/cookbooks/application_git/metadata.json new file mode 100644 index 0000000..32b4027 --- /dev/null +++ b/cookbooks/application_git/metadata.json @@ -0,0 +1 @@ +{"name":"application_git","version":"1.1.0","description":"A plugin for poise-application to deploy applications from git.","long_description":"# Application_Git Cookbook\n\n[![Build Status](https://img.shields.io/travis/poise/application_git.svg)](https://travis-ci.org/poise/application_git)\n[![Gem Version](https://img.shields.io/gem/v/poise-application-git.svg)](https://rubygems.org/gems/poise-application-git)\n[![Cookbook Version](https://img.shields.io/cookbook/v/application_git.svg)](https://supermarket.chef.io/cookbooks/application_git)\n[![Coverage](https://img.shields.io/codecov/c/github/poise/application_git.svg)](https://codecov.io/github/poise/application_git)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/application_git.svg)](https://gemnasium.com/poise/application_git)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nA [Chef](https://www.chef.io/) cookbook to handle deploying code from git when\nusing the [application cookbook](https://github.com/poise/application).\n\n## Quick Start\n\nTo deploy from a private GitHub repository:\n\n```ruby\napplication '/srv/myapp' do\n git 'git@github.com:example/myapp.git' do\n deploy_key chef_vault_item('deploy_keys', 'myapp')['key']\n end\nend\n```\n\n## Requirements\n\nChef 12 or newer is required.\n\n## Resources\n\n### `application_git`\n\nThe `application_git` resource deploys code from git. It extends the core `git`\nresource to support deploy keys and disabling strict host key verification.\n\n```ruby\napplication '/srv/myapp' do\n git 'git@github.com:example/myapp.git'\nend\n```\n\n#### Actions\n\nAll actions work the same as the core `git` resource.\n\n* `:sync` – Clone and checkout the requested revision *(default)*\n* `:checkout` – Checkout the request revision. If the repository isn't already\n cloned, this action does nothing.\n* `:export` – Export the repository without the `.git` folder.\n\n#### Properties\n\nAll properties from the core `git` resource work the same way with the following\nadditions:\n\n* `deploy_key` – SSH key to use with git. Can be specified either as a path to\n key file already created or as a string value containing the key directly.\n* `strict_ssh` – Enable strict SSH host key checking. *(default: false)*\n\n### DSL Usage\n\nThe `application_git` resource can be used directly as a replacement for the\ncore `git` resource:\n\n```ruby\napplication_git '/srv/myapp' do\n repository 'git@github.com:example/myapp.git'\n deploy_key chef_vault_item('deploy_keys', 'myapp')['key']\nend\n```\n\nWithin the `application` resource, a simplified DSL is available. As with other\n`application` plugins, the default name of the resource if unspecified is the\napplication path. The following two examples are equivalent:\n\n```ruby\napplication '/srv/myapp' do\n git do\n repository 'git@github.com:example/myapp.git'\n end\nend\n\napplication '/srv/myapp' do\n git 'git@github.com:example/myapp.git'\nend\n```\n\n## Sponsors\n\nDevelopment sponsored by [Chef Software](https://www.chef.io/), [Symonds & Son](http://symondsandson.com/), and [Orion](https://www.orionlabs.co/).\n\nThe Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/).\n\n## License\n\nCopyright 2015-2016, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainer":"Noah Kantrowitz","maintainer_email":"noah@coderanger.net","license":"Apache 2.0","platforms":{},"dependencies":{"git":">= 0.0.0","poise":"~> 2.0","application":"~> 5.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{}} \ No newline at end of file diff --git a/cookbooks/application_javascript/CHANGELOG.md b/cookbooks/application_javascript/CHANGELOG.md new file mode 100644 index 0000000..ed8506a --- /dev/null +++ b/cookbooks/application_javascript/CHANGELOG.md @@ -0,0 +1,5 @@ +# Application_Javascript Changelog + +## v1.0.0 + +Initial release! diff --git a/cookbooks/application_javascript/README.md b/cookbooks/application_javascript/README.md new file mode 100644 index 0000000..57745f0 --- /dev/null +++ b/cookbooks/application_javascript/README.md @@ -0,0 +1,132 @@ +# Application_Javascript Cookbook + +[![Build Status](https://img.shields.io/travis/poise/application_javascript.svg)](https://travis-ci.org/poise/application_javascript) +[![Gem Version](https://img.shields.io/gem/v/poise-application-javascript.svg)](https://rubygems.org/gems/poise-application-javascript) +[![Cookbook Version](https://img.shields.io/cookbook/v/application_javascript.svg)](https://supermarket.chef.io/cookbooks/application_javascript) +[![Coverage](https://img.shields.io/codecov/c/github/poise/application_javascript.svg)](https://codecov.io/github/poise/application_javascript) +[![Gemnasium](https://img.shields.io/gemnasium/poise/application_javascript.svg)](https://gemnasium.com/poise/application_javascript) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +A [Chef](https://www.chef.io/) cookbook to deploy server-side JavaScript +applications using Node.js or io.js. + +## Quick Start + +To deploy an Express application from git: + +```ruby +application '/srv/myapp' do + git 'https://github.com/example/myapp.git' + npm_install + npm_start +end +``` + +## Requirements + +Chef 12 or newer is required. + +## Resources + +### `application_javascript` + +The `application_javascript` resource installs a JavaScript runtime for the +deployment. + +```ruby +application '/srv/myapp' do + javascript '3' +end +``` + +All actions and properties are the same as the [`javascript_runtime` resource](https://github.com/poise/poise-javascript#javascript_runtime). + +### `application_javascript_service` + +The `application_javascript_javascript_service` resource creates a service for a +JavaScript command. + +```ruby +application '/srv/myapp' do + javascript_service 'main.js' +end +``` + +#### Actions + +* `:enable` – Create, enable and start the service. *(default)* +* `:disable` – Stop, disable, and destroy the service. +* `:start` – Start the service. +* `:stop` – Stop the service. +* `:restart` – Stop and then start the service. +* `:reload` – Send the configured reload signal to the service. + +#### Properties + +* `command` – Command to run. *(name attribute)* +* `path` – Base path for the application. *(default: application path)* +* `service_name` – Name of the service to create. *(default: auto-detect)* +# `user` – User to run the service as. *(default: application owner)* + +### `application_node_package` + +The `application_node_package` resource installs NPM packages for the deployment. + +```ruby +application '/srv/myapp' do + node_package 'grunt-cli' +end +``` + +All actions and properties are the same as the [`node_package` resource](https://github.com/poise/poise-javascript#node_package), +except that the `group` and `user` properties default to the application-level +data if not specified. + +### `application_npm_start` + +The `application_npm_start` resource creates a service for a JavaScript +application using `npm start`. + +```ruby +application '/srv/myapp' do + npm_start +end +``` + +#### Actions + +* `:enable` – Create, enable and start the service. *(default)* +* `:disable` – Stop, disable, and destroy the service. +* `:start` – Start the service. +* `:stop` – Stop the service. +* `:restart` – Stop and then start the service. +* `:reload` – Send the configured reload signal to the service. + +#### Properties + +* `path` – Base path for the application. *(default: name attribute)* +* `command` – NPM subcommand to run. *(default: start)* +* `service_name` – Name of the service to create. *(default: auto-detect)* +# `user` – User to run the service as. *(default: application owner)* + +## Sponsors + +Development sponsored by [Chef Software](https://www.chef.io/), [Symonds & Son](http://symondsandson.com/), and [Orion](https://www.orionlabs.co/). + +The Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/). + +## License + +Copyright 2015, Noah Kantrowitz + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript.rb new file mode 100644 index 0000000..d481a29 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript.rb @@ -0,0 +1,23 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseApplicationJavascript + autoload :AppMixin, 'poise_application_javascript/app_mixin' + autoload :Error, 'poise_application_javascript/error' + autoload :Resources, 'poise_application_javascript/resources' + autoload :VERSION, 'poise_application_javascript/version' +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/app_mixin.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/app_mixin.rb new file mode 100644 index 0000000..06d4705 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/app_mixin.rb @@ -0,0 +1,67 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/backports' +require 'poise/utils' +require 'poise_application/app_mixin' +require 'poise_javascript/javascript_command_mixin' + + +module PoiseApplicationJavascript + # A helper mixin for Javascript application resources and providers. + # + # @since 4.0.0 + module AppMixin + include Poise::Utils::ResourceProviderMixin + + # A helper mixin for Javascript application resources. + module Resource + include PoiseApplication::AppMixin::Resource + include PoiseJavascript::JavascriptCommandMixin::Resource + + # @!attribute parent_javascript + # Override the #parent_javascript from JavascriptCommandMixin to grok the + # application level parent as a default value. + # @return [PoiseJavascript::Resources::JavascriptRuntime::Resource, nil] + parent_attribute(:javascript, type: :javascript_runtime, optional: true, default: lazy { app_state_javascript.equal?(self) ? nil : app_state_javascript }) + + # @attribute app_state_javascript + # The application-level Javascript parent. + # @return [PoiseJavascript::Resources::JavascriptRuntime::Resource, nil] + def app_state_javascript(javascript=Poise::NOT_PASSED) + unless javascript == Poise::NOT_PASSED + app_state[:javascript] = javascript + end + app_state[:javascript] + end + + # A merged hash of environment variables for both the application state + # and parent javascript. + # + # @return [Hash] + def app_state_environment_javascript + env = app_state_environment + env = env.merge(parent_javascript.javascript_environment) if parent_javascript + env + end + end + + # A helper mixin for Javascript application providers. + module Provider + include PoiseApplication::AppMixin::Provider + end + end +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/cheftie.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/cheftie.rb new file mode 100644 index 0000000..01a8f69 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/cheftie.rb @@ -0,0 +1,17 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application_javascript/resources' diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/error.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/error.rb new file mode 100644 index 0000000..71fba33 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/error.rb @@ -0,0 +1,25 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application/error' + +module PoiseApplicationJavascript + # Base exception class for poise-application-javascript errors. + # + # @since 1.0.0 + class Error < PoiseApplication::Error + end +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources.rb new file mode 100644 index 0000000..32a7cd3 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources.rb @@ -0,0 +1,22 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application_javascript/resources/javascript' +require 'poise_application_javascript/resources/javascript_execute' +require 'poise_application_javascript/resources/javascript_service' +require 'poise_application_javascript/resources/node_package' +require 'poise_application_javascript/resources/npm_install' +require 'poise_application_javascript/resources/npm_start' diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/javascript.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/javascript.rb new file mode 100644 index 0000000..e52aabd --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/javascript.rb @@ -0,0 +1,64 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_javascript/resources/javascript_runtime' + +require 'poise_application_javascript/app_mixin' + + +module PoiseApplicationJavascript + module Resources + # (see Javascript::Resource) + # @since 1.0.0 + module Javascript + # An `application_javascript` resource to manage Javascript runtimes + # inside an Application cookbook deployment. + # + # @provides application_javascript + # @provides application_javascript_runtime + # @action install + # @action uninstall + # @example + # application '/app' do + # javascript '3' + # end + class Resource < PoiseJavascript::Resources::JavascriptRuntime::Resource + include PoiseApplicationJavascript::AppMixin + provides(:application_javascript) + # Need the double javascript for application resource rewriting. + provides(:application_javascript_runtime) + container_default(false) + subclass_providers! + + # We want to run the base class version of this, not the one from the + # mixin. HULK SMASH. + def npm_binary + self.class.superclass.instance_method(:npm_binary).bind(self).call + end + + # Set this resource as the app_state's parent javascript. + # + # @api private + def after_created + super.tap do |val| + app_state_javascript(self) + end + end + + end + end + end +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/javascript_execute.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/javascript_execute.rb new file mode 100644 index 0000000..f2d6656 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/javascript_execute.rb @@ -0,0 +1,88 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_javascript/resources/javascript_execute' + +require 'poise_application_javascript/app_mixin' + + +module PoiseApplicationJavascript + module Resources + # (see JavascriptExecute::Resource) + # @since 1.0.0 + module JavascriptExecute + # An `application_javascript_execute` resource to run Javascript commands inside an + # Application cookbook deployment. + # + # @provides application_javascript_execute + # @action run + # @example + # application '/srv/myapp' do + # javascript_execute 'setup.py install' + # end + class Resource < PoiseJavascript::Resources::JavascriptExecute::Resource + include PoiseApplicationJavascript::AppMixin + provides(:application_javascript_execute) + def initialize(*args) + super + # Clear some instance variables so my defaults work. + remove_instance_variable(:@cwd) + remove_instance_variable(:@group) + remove_instance_variable(:@user) + end + + # #!attribute cwd + # Override the default directory to be the app path if unspecified. + # @return [String] + attribute(:cwd, kind_of: [String, NilClass, FalseClass], default: lazy { parent && parent.path }) + + # #!attribute group + # Override the default group to be the app group if unspecified. + # @return [String, Integer] + attribute(:group, kind_of: [String, Integer, NilClass, FalseClass], default: lazy { parent && parent.group }) + + # #!attribute user + # Override the default user to be the app owner if unspecified. + # @return [String, Integer] + attribute(:user, kind_of: [String, Integer, NilClass, FalseClass], default: lazy { parent && parent.owner }) + end + + # The default provider for `application_javascript_execute`. + # + # @see Resource + # @provides application_javascript_execute + class Provider < PoiseJavascript::Resources::JavascriptExecute::Provider + provides(:application_javascript_execute) + + private + + # Override environment to add the application envivonrment instead. + # + # @return [Hash] + def environment + super.tap do |environment| + # Don't use the app_state_environment_javascript because we already have + # those values in place. + environment.update(new_resource.app_state_environment) + # Re-apply the resource environment for correct ordering. + environment.update(new_resource.environment) if new_resource.environment + end + end + end + + end + end +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/javascript_service.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/javascript_service.rb new file mode 100644 index 0000000..614b561 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/javascript_service.rb @@ -0,0 +1,59 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'chef/resource' +require 'poise' + +require 'poise_application_javascript/service_mixin' + + +module PoiseApplicationJavascript + module Resources + # (see JavascriptService::Resource) + # @since 1.0.0 + module JavascriptService + class Resource < Chef::Resource + include PoiseApplicationJavascript::ServiceMixin + provides(:application_javascript_service) + + # @!attribute command + # Command to run. + # @return [String] + attribute(:command, kind_of: String, name_attribute: true) + # @!attribute path + # Override {PoiseApplicationJavascript::ServiceMixin#path} to make it + # not the name_attribute. + # @return [String] + attribute(:path, kind_of: String, default: lazy { parent && parent.path }) + end + + class Provider < Chef::Provider + include PoiseApplicationJavascript::ServiceMixin + provides(:application_javascript_service) + + private + + # (see PoiseApplication::ServiceMixin#service_options) + def service_options(resource) + super + resource.javascript_command(new_resource.command) + end + + end + end + end +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/node_package.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/node_package.rb new file mode 100644 index 0000000..6cad223 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/node_package.rb @@ -0,0 +1,63 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_javascript/resources/node_package' + +require 'poise_application_javascript/app_mixin' + + +module PoiseApplicationJavascript + module Resources + # (see NodePackage::Resource) + # @since 1.0.0 + module NodePackage + # An `application_node_package` resource to install NPM packages inside + # an Application cookbook deployment. + # + # @provides application_node_package + # @action install + # @action upgrade + # @action remove + # @example + # application '/app' do + # node_package %w{grunt-cli gulp} + # end + class Resource < PoiseJavascript::Resources::NodePackage::Resource + include PoiseApplicationJavascript::AppMixin + provides(:application_node_package) + subclass_providers! + + def initialize(*args) + super + # For older Chef. + @resource_name = :application_node_package + end + + # #!attribute group + # Override the default group to be the app group if unspecified. + # @return [String, Integer] + attribute(:group, kind_of: [String, Integer, NilClass], default: lazy { parent && parent.group }) + + # #!attribute user + # Override the default user to be the app owner if unspecified. + # @return [String, Integer] + attribute(:user, kind_of: [String, Integer, NilClass], default: lazy { parent && parent.owner }) + + # @todo This should handle relative paths against parent.path. + end + end + end +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/npm_install.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/npm_install.rb new file mode 100644 index 0000000..4527df0 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/npm_install.rb @@ -0,0 +1,45 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_javascript/resources/npm_install' + +require 'poise_application_javascript/app_mixin' + + +module PoiseApplicationJavascript + module Resources + # (see NpmInstall::Resource) + # @since 1.0.0 + module NpmInstall + # An `application_npm_install` resource to install package + # dependencies inside an Application cookbook deployment. + # + # @provides application_npm_install + # @action install + # @example + # application '/app' do + # npm_install + # end + class Resource < PoiseJavascript::Resources::NpmInstall::Resource + include PoiseApplicationJavascript::AppMixin + provides(:application_npm_install) + subclass_providers! + + # @todo This should handle relative paths against parent.path. + end + end + end +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/npm_start.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/npm_start.rb new file mode 100644 index 0000000..0cf803b --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/resources/npm_start.rb @@ -0,0 +1,78 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'shellwords' + +require 'chef/provider' +require 'chef/resource' +require 'poise' + +require 'poise_application_javascript/service_mixin' + + +module PoiseApplicationJavascript + module Resources + # (see NpmStart::Resource) + # @since 1.0.0 + module NpmStart + # An `application_npm_start` resource to create a service for a Javascript + # application using `npm start`. + # + # @provides application_npm_start + # @action enable + # @action disable + # @action start + # @action stop + # @action restart + # @action reload + # @example + # application '/app' do + # npm_start + # end + class Resource < Chef::Resource + include PoiseApplicationJavascript::ServiceMixin + provides(:application_npm_start) + + # @!attribute command + # NPM sub-command to run. Defaults to `start`. + # @return [String, Array] + attribute(:command, kind_of: [String, Array], default: 'start') + end + + # The default provider for `application_npm_start`. + # + # @see Resource + # @provides application_npm_start + class Provider < Chef::Provider + include PoiseApplicationJavascript::ServiceMixin + provides(:application_npm_start) + + private + + # (see PoiseApplication::ServiceMixin#service_options) + def service_options(resource) + super + npm_cmd = [new_resource.npm_binary] + Array(new_resource.command) + resource.javascript_command(Shellwords.join(npm_cmd)) + # Make sure node is on $PATH because grrr. + new_path = [::File.dirname(new_resource.javascript), (new_resource.app_state_environment_javascript['PATH'] || ENV['PATH']).to_s].join(::File::PATH_SEPARATOR) + resource.environment['PATH'] = new_path + end + + end + end + end +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/service_mixin.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/service_mixin.rb new file mode 100644 index 0000000..f4359a1 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/service_mixin.rb @@ -0,0 +1,57 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/utils' +require 'poise_application/service_mixin' +require 'poise_languages/utils' + +require 'poise_application_javascript/app_mixin' + + +module PoiseApplicationJavascript + # A helper mixin for Javascript service resources and providers. + # + # @since 1.0.0 + module ServiceMixin + include Poise::Utils::ResourceProviderMixin + + # A helper mixin for Javascript service resources. + module Resource + include PoiseApplication::ServiceMixin::Resource + include PoiseApplicationJavascript::AppMixin::Resource + end + + # A helper mixin for Javascript service providers. + module Provider + include PoiseApplication::ServiceMixin::Provider + include PoiseApplicationJavascript::AppMixin::Provider + + # Set up the service for running Javascript stuff. + def service_options(resource) + super + # Closure scoping for #javascript_command below. + self_ = self + # Create a new singleton method that fills in `node` for you. + resource.define_singleton_method(:javascript_command) do |val| + resource.command("#{self_.new_resource.javascript} #{PoiseLanguages::Utils.absolute_command(val, path: self_.new_resource.app_state_environment_javascript['PATH'])}") + end + # Include env vars as needed. + resource.environment.update(new_resource.parent_javascript.javascript_environment) if new_resource.parent_javascript + end + + end + end +end diff --git a/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/version.rb b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/version.rb new file mode 100644 index 0000000..4162872 --- /dev/null +++ b/cookbooks/application_javascript/files/halite_gem/poise_application_javascript/version.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module PoiseApplicationJavascript + VERSION = '1.0.0' +end diff --git a/cookbooks/application_javascript/libraries/default.rb b/cookbooks/application_javascript/libraries/default.rb new file mode 100644 index 0000000..2c3ee48 --- /dev/null +++ b/cookbooks/application_javascript/libraries/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) +require "poise_application_javascript/cheftie" diff --git a/cookbooks/application_javascript/metadata.json b/cookbooks/application_javascript/metadata.json new file mode 100644 index 0000000..c09bcbe --- /dev/null +++ b/cookbooks/application_javascript/metadata.json @@ -0,0 +1 @@ +{"name":"application_javascript","version":"1.0.0","description":"A Chef cookbook for deploying server-side JavaScript application code.","long_description":"# Application_Javascript Cookbook\n\n[![Build Status](https://img.shields.io/travis/poise/application_javascript.svg)](https://travis-ci.org/poise/application_javascript)\n[![Gem Version](https://img.shields.io/gem/v/poise-application-javascript.svg)](https://rubygems.org/gems/poise-application-javascript)\n[![Cookbook Version](https://img.shields.io/cookbook/v/application_javascript.svg)](https://supermarket.chef.io/cookbooks/application_javascript)\n[![Coverage](https://img.shields.io/codecov/c/github/poise/application_javascript.svg)](https://codecov.io/github/poise/application_javascript)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/application_javascript.svg)](https://gemnasium.com/poise/application_javascript)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nA [Chef](https://www.chef.io/) cookbook to deploy server-side JavaScript\napplications using Node.js or io.js.\n\n## Quick Start\n\nTo deploy an Express application from git:\n\n```ruby\napplication '/srv/myapp' do\n git 'https://github.com/example/myapp.git'\n npm_install\n npm_start\nend\n```\n\n## Requirements\n\nChef 12 or newer is required.\n\n## Resources\n\n### `application_javascript`\n\nThe `application_javascript` resource installs a JavaScript runtime for the\ndeployment.\n\n```ruby\napplication '/srv/myapp' do\n javascript '3'\nend\n```\n\nAll actions and properties are the same as the [`javascript_runtime` resource](https://github.com/poise/poise-javascript#javascript_runtime).\n\n### `application_javascript_service`\n\nThe `application_javascript_javascript_service` resource creates a service for a\nJavaScript command.\n\n```ruby\napplication '/srv/myapp' do\n javascript_service 'main.js'\nend\n```\n\n#### Actions\n\n* `:enable` – Create, enable and start the service. *(default)*\n* `:disable` – Stop, disable, and destroy the service.\n* `:start` – Start the service.\n* `:stop` – Stop the service.\n* `:restart` – Stop and then start the service.\n* `:reload` – Send the configured reload signal to the service.\n\n#### Properties\n\n* `command` – Command to run. *(name attribute)*\n* `path` – Base path for the application. *(default: application path)*\n* `service_name` – Name of the service to create. *(default: auto-detect)*\n# `user` – User to run the service as. *(default: application owner)*\n\n### `application_node_package`\n\nThe `application_node_package` resource installs NPM packages for the deployment.\n\n```ruby\napplication '/srv/myapp' do\n node_package 'grunt-cli'\nend\n```\n\nAll actions and properties are the same as the [`node_package` resource](https://github.com/poise/poise-javascript#node_package),\nexcept that the `group` and `user` properties default to the application-level\ndata if not specified.\n\n### `application_npm_start`\n\nThe `application_npm_start` resource creates a service for a JavaScript\napplication using `npm start`.\n\n```ruby\napplication '/srv/myapp' do\n npm_start\nend\n```\n\n#### Actions\n\n* `:enable` – Create, enable and start the service. *(default)*\n* `:disable` – Stop, disable, and destroy the service.\n* `:start` – Start the service.\n* `:stop` – Stop the service.\n* `:restart` – Stop and then start the service.\n* `:reload` – Send the configured reload signal to the service.\n\n#### Properties\n\n* `path` – Base path for the application. *(default: name attribute)*\n* `command` – NPM subcommand to run. *(default: start)*\n* `service_name` – Name of the service to create. *(default: auto-detect)*\n# `user` – User to run the service as. *(default: application owner)*\n\n## Sponsors\n\nDevelopment sponsored by [Chef Software](https://www.chef.io/), [Symonds & Son](http://symondsandson.com/), and [Orion](https://www.orionlabs.co/).\n\nThe Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/).\n\n## License\n\nCopyright 2015, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainer":"YOUR_COMPANY_NAME","maintainer_email":"YOUR_EMAIL","license":"none","platforms":{},"dependencies":{"poise":"~> 2.0","application":"~> 5.0","poise-javascript":"~> 1.0","poise-service":"~> 1.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{}} \ No newline at end of file diff --git a/cookbooks/application_nodejs/LICENSE b/cookbooks/application_nodejs/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/cookbooks/application_nodejs/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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 [yyyy] [name of copyright owner] - -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/cookbooks/application_nodejs/README.md b/cookbooks/application_nodejs/README.md deleted file mode 100644 index 0a1dba4..0000000 --- a/cookbooks/application_nodejs/README.md +++ /dev/null @@ -1,64 +0,0 @@ -## Description - -This cookbook is designed to be able to describe and deploy Node.js web applications using Upstart. - -Note that this cookbook provides the Node-specific bindings for the `application` cookbook; you will find general documentation in that cookbook. - -## Requirements - -Chef 0.10.0 or higher required (for Chef environment use). - -Upstart 1.4 or higher for the use of `setuid` in the default Upstart configuration template. -This requirement can be worked around by specifying a custom template. - -The following Opscode cookbooks are dependencies: - -* [application](https://github.com/opscode-cookbooks/application) -* [nodejs](https://github.com/mdxp/nodejs-cookbook) - -## Resources/Providers - -The `nodejs` sub-resource LWRP deals with deploying Node.js applications using Upstart. It is not meant to be used by itself; make sure you are familiar with the `application` cookbook before proceeding. - -### Attribute Parameters - -- **npm**: If `true`, `npm` will be used to install the dependencies specified in `packages.json`. Defaults to `true`. -- **template**: The name of the template that will be used to create the Upstart configuration file. If specified, it will be looked up in the application cookbook. Defaults to `nodejs.upstart.conf.erb` from this cookbook. -- **entry_point**: The argument to pass to node. Defaults to `app.js`. - -## Usage - -Here is an example hello world application using [Express](http://expressjs.com): - -``` -application "hello-world" do - path "/srv/node-hello-world" - owner "www-data" - group "www-data" - packages ["git"] - - repository "git://github.com/visionmedia/express.git" - - nodejs do - entry_point "examples/hello-world" - end -end -``` - -## License and Author - -Author:: Conrad Kramer () - -Copyright 2013, Kramer Software Productions, LLC. - -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/cookbooks/application_nodejs/metadata.rb b/cookbooks/application_nodejs/metadata.rb deleted file mode 100644 index 66dde51..0000000 --- a/cookbooks/application_nodejs/metadata.rb +++ /dev/null @@ -1,12 +0,0 @@ -name "application_nodejs" -maintainer "Conrad Kramer" -maintainer_email "conrad@kramerapps.com" -license "Apache 2.0" -description "Deploys and configures Node.js applications" -long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "2.0.1" - -depends "nodejs" -depends "application" - -supports 'ubuntu', ">= 12.04" diff --git a/cookbooks/application_nodejs/providers/nodejs.rb b/cookbooks/application_nodejs/providers/nodejs.rb deleted file mode 100644 index e426f37..0000000 --- a/cookbooks/application_nodejs/providers/nodejs.rb +++ /dev/null @@ -1,130 +0,0 @@ -# -# Author:: Conrad Kramer -# Cookbook Name:: application_node -# Resource:: node -# -# Copyright:: 2013, Kramer Software Productions, LLC. -# -# 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. -# - -include Chef::DSL::IncludeRecipe - -action :before_compile do - - r = new_resource - - if new_resource.npm - include_recipe 'nodejs::npm' - end - - unless new_resource.restart_command - new_resource.restart_command do - - service "#{r.application.name}_nodejs" do - if platform?('ubuntu') && node[:platform_version].to_f >= 14.04 - provider Chef::Provider::Service::Systemd - else - provider Chef::Provider::Service::Upstart - end - supports :restart => true, :start => true, :stop => true - action [:enable, :restart] - end - - end - end - - new_resource.environment.update({ - 'NODE_ENV' => r.environment_name - }) - -end - -action :before_deploy do - - new_resource.environment['NODE_ENV'] = new_resource.environment_name - - r = new_resource - - service "#{r.application.name}_nodejs" do - if platform?('ubuntu') && node[:platform_version].to_f >= 14.04 - provider Chef::Provider::Service::Systemd - else - provider Chef::Provider::Service::Upstart - end - end - - if platform?('ubuntu') && node[:platform_version].to_f >= 14.04 - execute "systemctl daemon-reload" do - command "systemctl daemon-reload" - action :nothing - end - - template "#{r.application.name}_nodejs.systemd.service.erb" do - path "/lib/systemd/system/#{r.application.name}_nodejs.service" - source r.template ? r.template : 'nodejs.systemd.service.erb' - cookbook r.template ? r.cookbook_name.to_s : 'application_nodejs' - owner 'root' - group 'root' - mode '0644' - variables( - :user => r.owner, - :group => r.group, - :app_dir => r.release_path, - :entry => r.entry_point, - :environment => r.environment - ) - notifies :run, "execute[systemctl daemon-reload]", :delayed - notifies :restart, "service[#{r.application.name}_nodejs]", :delayed - end - else - template "#{new_resource.application.name}.upstart.conf" do - path "/etc/init/#{r.application.name}_nodejs.conf" - source r.template ? r.template : 'nodejs.upstart.conf.erb' - cookbook r.template ? r.cookbook_name.to_s : 'application_nodejs' - owner 'root' - group 'root' - mode '0644' - variables( - :user => r.owner, - :group => r.group, - :app_dir => r.release_path, - :entry => r.entry_point, - :environment => r.environment - ) - notifies :restart, "service[#{r.application.name}_nodejs]", :delayed - end - end -end - -action :before_migrate do - - if new_resource.npm - execute 'npm install' do - cwd new_resource.release_path - user new_resource.owner - group new_resource.group - environment new_resource.environment.merge({ 'HOME' => new_resource.shared_path }) - end - end - -end - -action :before_symlink do -end - -action :before_restart do -end - -action :after_restart do -end diff --git a/cookbooks/application_nodejs/templates/default/nodejs.systemd.service.erb b/cookbooks/application_nodejs/templates/default/nodejs.systemd.service.erb deleted file mode 100644 index 99fa0f0..0000000 --- a/cookbooks/application_nodejs/templates/default/nodejs.systemd.service.erb +++ /dev/null @@ -1,4 +0,0 @@ -[Service] -ExecStart=/usr/bin/env node <%= @entry %> -User=<%= @user %> -Group=<%= @group %> diff --git a/cookbooks/application_nodejs/templates/default/nodejs.upstart.conf.erb b/cookbooks/application_nodejs/templates/default/nodejs.upstart.conf.erb deleted file mode 100644 index ab1e8be..0000000 --- a/cookbooks/application_nodejs/templates/default/nodejs.upstart.conf.erb +++ /dev/null @@ -1,19 +0,0 @@ -#!upstart -description "Node.js Application Server" - -start on (local-filesystems and net-device-up IFACE!=lo) -stop on [!12345] - -console log - -<% @environment.each do |key, value| -%> -env <%= key %>="<%= value %>" -<% end -%> -<% unless @user.nil? -%> -setuid <%= @user %> -<% end -%> -<% unless @group.nil? -%> -setgid <%= @group %> -<% end -%> -chdir <%= @app_dir %> -exec /usr/bin/env node <%= @entry %> diff --git a/cookbooks/application_ruby/CHANGELOG.md b/cookbooks/application_ruby/CHANGELOG.md new file mode 100644 index 0000000..5200030 --- /dev/null +++ b/cookbooks/application_ruby/CHANGELOG.md @@ -0,0 +1,86 @@ +# Application_Ruby Changelog + +## v4.0.1 + +* Correct `gem_binary` results for `application_ruby`. + +## v4.0.0 + +* Massive rewrite on top of newer Chef patterns. See the 4.0 README for details. + +## v3.0.2 + +* No changes, bumping version to get bits in various places in sync. + +## v3.0.0 + +* Major version bump. Breaking backwards compatibility with Chef 10. + +## v2.2.0 + +* [COOK-3895](https://tickets.opscode.com/browse/COOK-3895) - application_ruby use_omnibus_ruby attr needs to default to false. +* [COOK-3894](https://tickets.opscode.com/browse/COOK-3894) - application_ruby cookbook needs version bump to pick up application v4.0 cookbook. +* [COOK-2079](https://tickets.opscode.com/browse/COOK-2079) - Attempting to touch restart.txt should not cause a chef-client run to fail. + +## v2.1.4 + +* [COOK-3625](https://tickets.opscode.com/browse/COOK-3625) - Fix an issue where unicorn fails when node does not provide cpu count. + +## v2.1.2 + +* [COOK-3616](https://tickets.opscode.com/browse/COOK-3616) - Simplify log symlinking for rails apps. + +## v2.1.0 + +* [COOK-3367](https://tickets.opscode.com/browse/COOK-3367) - Support more of unicorn's configuration. +* [COOK-3124](https://tickets.opscode.com/browse/COOK-3124) - Add `memcached_template` attribute to so alternative templates may be used. + +## v2.0.0 + +### Bug + +* [COOK-3306]: Multiple Memory Leaks in Application Cookbook. +* [COOK-3219]: `application_ruby` cookbook bundle install in 1.9.3-based omnibus installs 1.9.x gems into ruby 2.0 apps. + +## v1.1.4 + +* [COOK-2806]: Including `passenger_apache2::mod_rails` does not enable passenger. + +## v1.1.2 + +* [COOK-2638]: cookbook attribute is not treated as a string when specifying `database_yml_template`. +* [COOK-2525]: application_ruby: split runit template into multiple lines. + +## v1.1.0 + +* [COOK-2362] - `application_ruby` unicorn uses `run_restart`. +* [COOK-2363] - `application_ruby` unicorn should set `log_template_name` and `run_template_name`. + +## v1.0.10 + +* [COOK-2260] - pin runit version. + +## v1.0.8 + +* [COOK-2159] - cookbook attribute is not treated as a string. + +## v1.0.6 + +* [COOK-1481] - unicorn provider in application_ruby cookbook should run its restart command as root. + +## v1.0.4 + +* [COOK-1572] - allow specification of 'bundle' command via attribute. + +## v1.0.2 + +* [COOK-1360] - fix typo in README. +* [COOK-1374] - use runit attribute in unicorn run script. +* [COOK-1408] - use user and group from parent resource for runit service. + +## v1.0.0 + +* [COOK-1247] - Initial release - relates to COOK-634. +* [COOK-1248] - special cases memcached. +* [COOK-1258] - Precompile assets for Rails 3. +* [COOK-1297] - Unicorn sub-resource should allow strings for 'port' attribute. diff --git a/cookbooks/application_ruby/README.md b/cookbooks/application_ruby/README.md new file mode 100644 index 0000000..98ea3e3 --- /dev/null +++ b/cookbooks/application_ruby/README.md @@ -0,0 +1,271 @@ +# Application_Ruby Cookbook + +[![Build Status](https://img.shields.io/travis/poise/application_ruby.svg)](https://travis-ci.org/poise/application_ruby) +[![Gem Version](https://img.shields.io/gem/v/poise-application-ruby.svg)](https://rubygems.org/gems/poise-application-ruby) +[![Cookbook Version](https://img.shields.io/cookbook/v/application_ruby.svg)](https://supermarket.chef.io/cookbooks/application_ruby) +[![Coverage](https://img.shields.io/codecov/c/github/poise/application_ruby.svg)](https://codecov.io/github/poise/application_ruby) +[![Gemnasium](https://img.shields.io/gemnasium/poise/application_ruby.svg)](https://gemnasium.com/poise/application_ruby) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +A [Chef](https://www.chef.io/) cookbook to deploy Ruby applications. + +## Quick Start + +To deploy a Rails application from git: + +```ruby +application '/srv/myapp' do + git 'https://github.com/example/myapp.git' + bundle_install do + deployment true + without %w{development test} + end + rails do + database 'sqlite3:///db.sqlite3' + secret_token 'd78fe08df56c9' + migrate true + end + unicorn do + port 8000 + end +end +``` + +## Requirements + +Chef 12 or newer is required. + +## Resources + +### `application_bundle_install` + +The `application_bundle_install` resource installs gems using Bundler for a +deployment. + +```ruby +application '/srv/myapp' do + bundle_install do + deployment true + without %w{development test} + end +end +``` + +All actions and properties are the same as the [`bundle_install` resource](https://github.com/poise/poise-ruby#bundle_install). + +### `application_rackup` + +The `application_rackup` resource creates a service for `rackup`. + +```ruby +application '/srv/myapp' do + rackup do + port 8000 + end +end +``` + +#### Actions + +* `:enable` – Create, enable and start the service. *(default)* +* `:disable` – Stop, disable, and destroy the service. +* `:start` – Start the service. +* `:stop` – Stop the service. +* `:restart` – Stop and then start the service. +* `:reload` – Send the configured reload signal to the service. + +#### Properties + +* `path` – Base path for the application. *(name attribute)* +* `port` – Port to listen on. *(default: 80)* +* `service_name` – Name of the service to create. *(default: auto-detect)* +# `user` – User to run the service as. *(default: application owner)* + +### `application_rails` + +The `application_rails` resource + +```ruby +application '/srv/myapp' do + rails do + database 'sqlite3:///db.sqlite3' + secret_token 'd78fe08df56c9' + migrate true + end +end +``` + +#### Actions + +* `:deploy` – Create config files and run required deployments steps. *(default)* + +#### Properties + +* `path` – Base path for the application. *(name attribute)* +* `database` – Database settings for Rails. See [the database section + below](#database-parameters) for more information. *(option collector)* +* `migrate` – Run database migrations. *(default: false)* +* `precompile_assets` – Run `rake assets:precompile`. *(default: auto-detect)() +* `rails_env` – Rails environment name. *(default: node.chef_environment)* +* `secret_token` – Secret token for Rails session verification et al. +* `secrets_mode` – Secrets configuration mode. Set to `:yaml` to generate a + Rails 4.2 secrets.yml. Set to `:initializer` to update + `config/initializers/secret_token.rb`. *(default: auto-detect)* + +**NOTE:** At this time `secrets_mode :initializer` is not implemented. + +#### Database Parameters + +The database parameters can be set in three ways: URL, hash, and block. + +If you have a single URL for the parameters, you can pass it directly to +`database`: + +```ruby +rails do + database 'mysql2://myuser@dbhost/myapp' +end +``` + +Passing a single URL will also set the `$DATABASE_URL` environment variable +automatically for compatibility with Heroku-based applications. + +As with other option collector resources, you can pass individual settings as +either a hash or block: + +```ruby +rails do + database do + adapter 'mysql2' + username 'myuser' + host 'dbhost' + database 'myapp' + end +end + +rails do + database({ + adapter: 'mysql2', + username: 'myuser', + host: 'dbhost', + database: 'myapp', + }) +end +``` + +### `application_ruby` + +The `application_ruby` resource installs a Ruby runtime for the deployment. + +```ruby +application '/srv/myapp' do + ruby '2.2' +end +``` + +All actions and properties are the same as the [`ruby_runtime` resource](https://github.com/poise/poise-ruby#ruby_runtime). + +### `application_ruby_gem` + +The `application_ruby_gem` resource installs Ruby gems for the deployment. + +```ruby +application '/srv/myapp' do + ruby_gem 'rake' +end +``` + +All actions and properties are the same as the [`ruby_gem` resource](https://github.com/poise/poise-ruby#ruby_gem). + +### `application_ruby_execute` + +The `application_ruby_execute` resource runs Ruby commands for the deployment. + +```ruby +application '/srv/myapp' do + ruby_execute 'rake' +end +``` + +All actions and properties are the same as the [`ruby_execute` resource](https://github.com/poise/poise-ruby#ruby_execute), +except that the `cwd`, `environment`, `group`, and `user` properties default to +the application-level data if not specified. + +### `application_thin` + +The `application_thin` resource creates a service for `thin`. + +```ruby +application '/srv/myapp' do + thin do + port 8000 + end +end +``` + +#### Actions + +* `:enable` – Create, enable and start the service. *(default)* +* `:disable` – Stop, disable, and destroy the service. +* `:start` – Start the service. +* `:stop` – Stop the service. +* `:restart` – Stop and then start the service. +* `:reload` – Send the configured reload signal to the service. + +#### Properties + +* `path` – Base path for the application. *(name attribute)* +* `config_path` – Path to a Thin configuration file. +* `port` – Port to listen on. *(default: 80)* +* `service_name` – Name of the service to create. *(default: auto-detect)* +# `user` – User to run the service as. *(default: application owner)* + +### `application_unicorn` + +The `application_unicorn` resource creates a service for `unicorn`. + +```ruby +application '/srv/myapp' do + unicorn do + port 8000 + end +end +``` + +#### Actions + +* `:enable` – Create, enable and start the service. *(default)* +* `:disable` – Stop, disable, and destroy the service. +* `:start` – Start the service. +* `:stop` – Stop the service. +* `:restart` – Stop and then start the service. +* `:reload` – Send the configured reload signal to the service. + +#### Properties + +* `path` – Base path for the application. *(name attribute)* +* `port` – Port to listen on. *(default: 80)* +* `service_name` – Name of the service to create. *(default: auto-detect)* +# `user` – User to run the service as. *(default: application owner)* + +## Sponsors + +Development sponsored by [Chef Software](https://www.chef.io/), [Symonds & Son](http://symondsandson.com/), and [Orion](https://www.orionlabs.co/). + +The Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/). + +## License + +Copyright 2015, Noah Kantrowitz + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby.rb new file mode 100644 index 0000000..7d480bf --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby.rb @@ -0,0 +1,24 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseApplicationRuby + autoload :AppMixin, 'poise_application_ruby/app_mixin' + autoload :Error, 'poise_application_ruby/error' + autoload :Resources, 'poise_application_ruby/resources' + autoload :ServiceMixin, 'poise_application_ruby/service_mixin' + autoload :VERSION, 'poise_application_ruby/version' +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/app_mixin.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/app_mixin.rb new file mode 100644 index 0000000..d377f7a --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/app_mixin.rb @@ -0,0 +1,92 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/backports' +require 'poise/utils' +require 'poise_application/app_mixin' +require 'poise_ruby/ruby_command_mixin' + + +module PoiseApplicationRuby + # A helper mixin for Ruby application resources and providers. + # + # @since 4.0.0 + module AppMixin + include Poise::Utils::ResourceProviderMixin + + # A helper mixin for Ruby application resources. + module Resource + include PoiseApplication::AppMixin::Resource + include PoiseRuby::RubyCommandMixin::Resource + + # @!attribute parent_ruby + # Override the #parent_ruby from RubyCommandMixin to grok the + # application level parent as a default value. + # @return [PoiseRuby::Resources::RubyRuntime::Resource, nil] + parent_attribute(:ruby, type: :ruby_runtime, optional: true, default: lazy { app_state_ruby.equal?(self) ? nil : app_state_ruby }) + + # @!attribute parent_bundle + # Parent bundle install context. + # @return [PoiseRuby::Resources::BundleInstall::Resource, nil] + parent_attribute(:bundle, type: :ruby_runtime, optional: true, auto: false, default: lazy { app_state_bundle.equal?(self) ? nil : app_state_bundle }) + + # @attribute app_state_ruby + # The application-level Ruby parent. + # @return [PoiseRuby::Resources::RubyRuntime::Resource, nil] + def app_state_ruby(ruby=Poise::NOT_PASSED) + unless ruby == Poise::NOT_PASSED + app_state[:ruby] = ruby + end + app_state[:ruby] + end + + # @attribute app_state_bundle + # The application-level Bundle parent. + # @return [PoiseRuby::Resources::BundleInstall::Resource, nil] + def app_state_bundle(bundle=Poise::NOT_PASSED) + unless bundle == Poise::NOT_PASSED + app_state[:bundle] = bundle + end + app_state[:bundle] + end + + # A merged hash of environment variables for both the application state + # and parent ruby. + # + # @return [Hash] + def app_state_environment_ruby + env = app_state_environment + env = env.merge(parent_ruby.ruby_environment) if parent_ruby + env['BUNDLE_GEMFILE'] = parent_bundle.gemfile_path if parent_bundle + env + end + + # Update ruby_from_parent to transfer {#parent_bundle} too. + # + # @param resource [Chef::Resource] Resource to inherit from. + # @return [void] + def ruby_from_parent(resource) + super + parent_bundle(resource.parent_bundle) if resource.parent_bundle + end + end + + # A helper mixin for Ruby application providers. + module Provider + include PoiseApplication::AppMixin::Provider + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/cheftie.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/cheftie.rb new file mode 100644 index 0000000..ca8eb03 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/cheftie.rb @@ -0,0 +1,17 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application_ruby/resources' diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/error.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/error.rb new file mode 100644 index 0000000..ae21fa0 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/error.rb @@ -0,0 +1,25 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application/error' + +module PoiseApplicationRuby + # Base exception class for poise-application-ruby errors. + # + # @since 4.0.0 + class Error < PoiseApplication::Error + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources.rb new file mode 100644 index 0000000..4b58b49 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources.rb @@ -0,0 +1,24 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_application_ruby/resources/bundle_install' +require 'poise_application_ruby/resources/rackup' +require 'poise_application_ruby/resources/rails' +require 'poise_application_ruby/resources/ruby' +require 'poise_application_ruby/resources/ruby_execute' +require 'poise_application_ruby/resources/ruby_gem' +require 'poise_application_ruby/resources/thin' +require 'poise_application_ruby/resources/unicorn' diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/bundle_install.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/bundle_install.rb new file mode 100644 index 0000000..d6ccd82 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/bundle_install.rb @@ -0,0 +1,54 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_ruby/resources/bundle_install' + +require 'poise_application_ruby/app_mixin' + + +module PoiseApplicationRuby + module Resources + # (see BundleInstall::Resource) + # @since 4.0.0 + module BundleInstall + # An `application_bundle_install` resource to install a + # [Bundler](http://bundler.io/) Gemfile in a web application. + # + # @note + # This resource is not idempotent itself, it will always run `bundle + # install`. + # @example + # application '/srv/my_app' do + # bundle_install + # end + class Resource < PoiseRuby::Resources::BundleInstall::Resource + include PoiseApplicationRuby::AppMixin + provides(:application_bundle_install) + subclass_providers! + + # Set this resource as the app_state's parent bundle. + # + # @api private + def after_created + super.tap do |val| + app_state_bundle(self) + end + end + end + + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/rackup.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/rackup.rb new file mode 100644 index 0000000..8b29730 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/rackup.rb @@ -0,0 +1,70 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'chef/resource' + +require 'poise_application_ruby/service_mixin' + + +module PoiseApplicationRuby + module Resources + # (see Rackup::Resource) + # @since 4.0.0 + module Rackup + class Resource < Chef::Resource + include PoiseApplicationRuby::ServiceMixin + provides(:application_rackup) + + # @!attribute port + # TCP port to listen on. Defaults to 80. + # @return [String, Integer] + attribute(:port, kind_of: [String, Integer], default: 80) + end + + class Provider < Chef::Provider + include PoiseApplicationRuby::ServiceMixin + provides(:application_rackup) + + private + + # Find the path to the config.ru. If the resource path was to a + # directory, apparent /config.ru. + # + # @return [String] + def configru_path + @configru_path ||= if ::File.directory?(new_resource.path) + ::File.join(new_resource.path, 'config.ru') + else + new_resource.path + end + end + + # Set up service options for rackup. + # + # @param resource [PoiseService::Resource] Service resource. + # @return [void] + def service_options(resource) + super + resource.ruby_command("rackup --port #{new_resource.port}") + resource.directory(::File.dirname(configru_path)) + # Older versions of rackup ignore all signals. + resource.stop_signal('KILL') + end + end + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/rails.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/rails.rb new file mode 100644 index 0000000..3ac56f9 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/rails.rb @@ -0,0 +1,260 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'chef/resource' + +require 'poise_application_ruby/app_mixin' + + +module PoiseApplicationRuby + module Resources + # (see Rails::Resource) + module Rails + # An `application_rails` resource to configure Ruby on Rails applications. + # + # @since 4.0.0 + # @provides application_rails + # @action deploy + # @example + # application '/srv/myapp' do + # git '...' + # bundle_install + # rails do + # database do + # host node['rails_host'] + # end + # end + # unicorn do + # port 8080 + # end + # end + class Resource < Chef::Resource + include PoiseApplicationRuby::AppMixin + provides(:application_rails) + actions(:deploy) + + # @!attribute database + # Option collector attribute for Rails database configuration. + # @return [Hash] + # @example Setting via block + # database do + # adapter 'postgresql' + # database 'blog' + # end + # @example Setting via URL + # database 'postgresql://localhost/blog' + attribute(:database, option_collector: true, parser: :parse_database_url) + # @!attribute database_config + # Template content attribute for the contents of database.yml. + # @todo Redo this doc to cover the actual attributes created. + # @return [Poise::Helpers::TemplateContent] + attribute(:database_config, template: true, default_source: 'database.yml.erb', default_options: lazy { default_database_options }) + # @!attribute migrate + # Run database migrations. This is a bad idea for real apps. Please + # do not use it. + # @return [Boolean] + attribute(:migrate, equal_to: [true, false], default: false) + # @!attribute precompile_assets + # Set to true to run rake assets:precompile. By default will try to + # auto-detect if Sprockets is in use by looking at config/initializers. + # @see #default_precompile_assets + # @return [Boolean] + attribute(:precompile_assets, equal_to: [true, false], default: lazy { default_precompile_assets }) + # @!attribute rails_env + # Rails environment name. Defaults to the Chef environment name or + # `production` if none is set. + # @see #default_rails_env + # @return [String] + attribute(:rails_env, kind_of: String, default: lazy { default_rails_env }) + # @!attribute secret_token + # Secret token for Rails session verification and other purposes. On + # Rails 4.2 this will be used for secret_key_base. If not set, no + # secrets configuration is written. + # @return [String] + attribute(:secret_token, kind_of: [String, FalseClass]) + # @!attribute secrets_config + # Template content attribute for the contents of secrets.yml. Only + # used when secrets_mode is :yaml. + # @todo Redo this doc to cover the actual attributes created. + # @return [Poise::Helpers::TemplateContent] + attribute(:secrets_config, template: true, default_source: 'secrets.yml.erb', default_options: lazy { default_secrets_options }) + # @!attribute secrets_mode + # Secrets configuration mode. Set to `:yaml` to generate a Rails 4.2 + # secrets.yml. Set to `:initializer` to update + # `config/initializers/secret_token.rb`. If unspecified this is + # auto-detected based on what files exist. + # @return [Symbol] + attribute(:secrets_mode, equal_to: [:yaml, :initializer], default: lazy { default_secrets_mode }) + + private + + # Check if we should run the precompile by default. Looks for the + # assets initializer because that is not present with --skip-sprockets. + # + # @return [Boolean] + def default_precompile_assets + ::File.exists?(::File.join(path, 'config', 'initializers', 'assets.rb')) + end + + # Check the default environment name. + # + # @return [String] + def default_rails_env + node.chef_environment == '_default' ? 'production' : node.chef_environment + end + + # Format a single URL for the database.yml + # + # @return [Hash] + def parse_database_url(url) + {'url' => url} + end + + # Default template variables for the database.yml. + # + # @return [Hash] + def default_database_options + db_config = {'encoding' => 'utf8', 'reconnect' => true, 'pool' => 5}.merge(database) + { + config: { + rails_env => db_config + }, + } + end + + # Check which secrets configuration mode is in use based on files. + # + # @return [Symbol] + def default_secrets_mode + ::File.exists?(::File.join(path, 'config', 'initializers', 'secret_token.rb')) ? :initialize : :yaml + end + + # Default template variables for the secrets.yml. + # + # @return [Hash] + def default_secrets_options + { + config: { + rails_env => { + 'secret_key_base' => secret_token, + } + }, + } + end + end + + # Provider for `application_rails`. + # + # @since 4.0.0 + # @see Resource + # @provides application_rails + class Provider < Chef::Provider + include PoiseApplicationRuby::AppMixin + provides(:application_rails) + + # `deploy` action for `application_rails`. Ensure all configuration + # files are created and other deploy tasks resolved. + # + # @return [void] + def action_deploy + set_state + notifying_block do + write_database_yml unless new_resource.database.empty? + write_secrets_config if new_resource.secret_token + precompile_assets if new_resource.precompile_assets + run_migrations if new_resource.migrate + end + end + + private + + # Set app_state variables for future services et al. + def set_state + new_resource.app_state_environment[:RAILS_ENV] = new_resource.rails_env + new_resource.app_state_environment[:DATABASE_URL] = new_resource.database['url'] if new_resource.database['url'] + end + + # Create a database.yml config file. + def write_database_yml + file ::File.join(new_resource.path, 'config', 'database.yml') do + user new_resource.parent.owner + group new_resource.parent.group + mode '640' + content new_resource.database_config_content + end + end + + # Dispatch to the correct config writer based on the mode. + def write_secrets_config + case new_resource.secrets_mode + when :yaml + write_secrets_yml + when :initializer + write_secrets_initializer + end + end + + # Write a Rails 4.2-style secrets.yml. + def write_secrets_yml + file ::File.join(new_resource.path, 'config', 'secrets.yml') do + user new_resource.parent.owner + group new_resource.parent.group + mode '640' + content new_resource.secrets_config_content + end + end + + # In-place update a config/initializers/secret_token.rb file. + def write_secrets_initializer + # @todo Implement initalizer-style secret support. + raise NotImplementedError.new('Sorry, intializer-style secrets loading is not yet supported.') + end + + # Precompile assets using the rake task. + def precompile_assets + # Currently this will always run so the resource will always update :-/ + # Better fix would be to shell_out! and parse the output? + ruby_execute 'rake assets:precompile' do + command %w{rake assets:precompile} + user new_resource.parent.owner + group new_resource.parent.group + cwd new_resource.parent.path + environment new_resource.app_state_environment + ruby_from_parent new_resource + parent_bundle new_resource.parent_bundle if new_resource.parent_bundle + end + end + + # Run database migrations using the rake task. + def run_migrations + # Currently this will always run so the resource will always update :-/ + # Better fix would be to shell_out! and parse the output? + ruby_execute 'rake db:migrate' do + command %w{rake db:migrate} + user new_resource.parent.owner + group new_resource.parent.group + cwd new_resource.parent.path + environment new_resource.app_state_environment + ruby_from_parent new_resource + parent_bundle new_resource.parent_bundle if new_resource.parent_bundle + end + end + + end + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/ruby.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/ruby.rb new file mode 100644 index 0000000..2519699 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/ruby.rb @@ -0,0 +1,63 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_ruby/resources/ruby_runtime' + +require 'poise_application_ruby/app_mixin' + + +module PoiseApplicationRuby + module Resources + # (see Ruby::Resource) + # @since 4.0.0 + module Ruby + # An `application_ruby` resource to manage Ruby runtimes + # inside an Application cookbook deployment. + # + # @provides application_ruby + # @provides application_ruby_runtime + # @action install + # @action uninstall + # @example + # application '/app' do + # ruby '2' + # end + class Resource < PoiseRuby::Resources::RubyRuntime::Resource + include PoiseApplicationRuby::AppMixin + provides(:application_ruby) + provides(:application_ruby_runtime) + container_default(false) + subclass_providers! + + # Rebind the parent class #gem_binary instead of the one from + # RubyCommandMixin (by way of AppMixin) + def gem_binary(*args, &block) + self.class.superclass.instance_method(:gem_binary).bind(self).call(*args, &block) + end + + # Set this resource as the app_state's parent ruby. + # + # @api private + def after_created + super.tap do |val| + app_state_ruby(self) + end + end + + end + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/ruby_execute.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/ruby_execute.rb new file mode 100644 index 0000000..8ca2b19 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/ruby_execute.rb @@ -0,0 +1,89 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_ruby/resources/ruby_execute' + +require 'poise_application_ruby/app_mixin' + + +module PoiseApplicationRuby + module Resources + # (see RubyExecute::Resource) + # @since 4.0.0 + module RubyExecute + # An `application_ruby_execute` resource to run Ruby commands inside an + # Application cookbook deployment. + # + # @provides application_ruby_execute + # @action run + # @example + # application '/srv/myapp' do + # ruby_execute 'rake build' + # end + class Resource < PoiseRuby::Resources::RubyExecute::Resource + include PoiseApplicationRuby::AppMixin + provides(:application_ruby_execute) + + def initialize(*args) + super + # Clear some instance variables so my defaults work. + remove_instance_variable(:@cwd) + remove_instance_variable(:@group) + remove_instance_variable(:@user) + end + + # #!attribute cwd + # Override the default directory to be the app path if unspecified. + # @return [String] + attribute(:cwd, kind_of: [String, NilClass, FalseClass], default: lazy { parent && parent.path }) + + # #!attribute group + # Override the default group to be the app group if unspecified. + # @return [String, Integer] + attribute(:group, kind_of: [String, Integer, NilClass, FalseClass], default: lazy { parent && parent.group }) + + # #!attribute user + # Override the default user to be the app owner if unspecified. + # @return [String, Integer] + attribute(:user, kind_of: [String, Integer, NilClass, FalseClass], default: lazy { parent && parent.owner }) + end + + # The default provider for `application_ruby_execute`. + # + # @see Resource + # @provides application_ruby_execute + class Provider < PoiseRuby::Resources::RubyExecute::Provider + provides(:application_ruby_execute) + + private + + # Override environment to add the application envivonrment instead. + # + # @return [Hash] + def environment + super.tap do |environment| + # Don't use the app_state_environment_ruby because we already have + # those values in place. + environment.update(new_resource.app_state_environment) + # Re-apply the resource environment for correct ordering. + environment.update(new_resource.environment) if new_resource.environment + end + end + end + + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/ruby_gem.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/ruby_gem.rb new file mode 100644 index 0000000..6a637e2 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/ruby_gem.rb @@ -0,0 +1,46 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_ruby/resources/ruby_gem' + +require 'poise_application_ruby/app_mixin' + + +module PoiseApplicationRuby + module Resources + # (see RubyGem::Resource) + # @since 4.0.0 + module RubyGem + # An `application_ruby_gem` resource to install Ruby gems inside an + # Application cookbook deployment. + # + # @provides application_ruby_gem + # @action install + # @action upgrade + # @action remove + # @example + # application '/srv/myapp' do + # ruby_gem 'rack' + # end + class Resource < PoiseRuby::Resources::RubyGem::Resource + include PoiseApplicationRuby::AppMixin + provides(:application_ruby_gem) + subclass_providers! + end + + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/thin.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/thin.rb new file mode 100644 index 0000000..a106137 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/thin.rb @@ -0,0 +1,64 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'chef/resource' + +require 'poise_application_ruby/service_mixin' + + +module PoiseApplicationRuby + module Resources + # (see Thin::Resource) + # @since 4.0.0 + module Thin + class Resource < Chef::Resource + include PoiseApplicationRuby::ServiceMixin + provides(:application_thin) + + attribute(:port, kind_of: [String, Integer], default: 80) + attribute(:config_path, kind_of: String) + end + + class Provider < Chef::Provider + include PoiseApplicationRuby::ServiceMixin + provides(:application_thin) + + private + + # Find the path to the config.ru. If the resource path was to a + # directory, apparent /config.ru. + # + # @return [String] + def configru_path + @configru_path ||= if ::File.directory?(new_resource.path) + ::File.join(new_resource.path, 'config.ru') + else + new_resource.path + end + end + + # (see PoiseApplication::ServiceMixin#service_options) + def service_options(resource) + super + cmd = "thin --rackup #{configru_path} --port #{new_resource.port}" + cmd << " --config #{::File.expand_path(new_resource.config_path, new_resource.path)}" if new_resource.config_path + resource.ruby_command(cmd) + end + end + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/unicorn.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/unicorn.rb new file mode 100644 index 0000000..3053d3e --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/resources/unicorn.rb @@ -0,0 +1,87 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'chef/resource' + +require 'poise_application_ruby/service_mixin' + + +module PoiseApplicationRuby + module Resources + # (see Unicorn::Resource) + # @since 4.0.0 + module Unicorn + # An `application_unicorn` resource to manage a unicorn web application + # server. + # + # @since 4.0.0 + # @provides application_unicorn + # @action enable + # @action disable + # @action start + # @action stop + # @action restart + # @action reload + # @example + # application '/srv/myapp' do + # git '...' + # bundle_install + # unicorn do + # port 8080 + # end + # end + class Resource < Chef::Resource + include PoiseApplicationRuby::ServiceMixin + provides(:application_unicorn) + + # @!attribute port + # Port to bind to. + attribute(:port, kind_of: [String, Integer], default: 80) + end + + # Provider for `application_unicorn`. + # + # @since 4.0.0 + # @see Resource + # @provides application_unicorn + class Provider < Chef::Provider + include PoiseApplicationRuby::ServiceMixin + provides(:application_unicorn) + + private + + # Find the path to the config.ru. If the resource path was to a + # directory, apparent /config.ru. + # + # @return [String] + def configru_path + @configru_path ||= if ::File.directory?(new_resource.path) + ::File.join(new_resource.path, 'config.ru') + else + new_resource.path + end + end + + # Set service resource options. + def service_options(resource) + super + resource.ruby_command("unicorn --port #{new_resource.port} #{configru_path}") + end + end + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/service_mixin.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/service_mixin.rb new file mode 100644 index 0000000..08ff095 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/service_mixin.rb @@ -0,0 +1,66 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/utils' +require 'poise_application/service_mixin' +require 'poise_languages/utils' +require 'poise_ruby/bundler_mixin' + +require 'poise_application_ruby/app_mixin' + + +module PoiseApplicationRuby + # A helper mixin for Ruby service resources and providers. + # + # @since 4.0.0 + module ServiceMixin + include Poise::Utils::ResourceProviderMixin + + # A helper mixin for Ruby service resources. + module Resource + include PoiseApplication::ServiceMixin::Resource + include PoiseApplicationRuby::AppMixin::Resource + end + + # A helper mixin for Ruby service providers. + module Provider + include PoiseApplication::ServiceMixin::Provider + include PoiseApplicationRuby::AppMixin::Provider + include PoiseRuby::BundlerMixin + + # Set up the service for running Ruby stuff. + def service_options(resource) + super + # Closure scoping for #ruby_command below. + self_ = self + # Create a new singleton method that fills in Python for you. + resource.define_singleton_method(:ruby_command) do |val| + path = self_.new_resource.app_state_environment_ruby['PATH'] + cmd = if self_.new_resource.parent_bundle + bundle_exec_command(val, path: path) + else + "#{self_.new_resource.ruby} #{PoiseLanguages::Utils.absolute_command(val, path: path)}" + end + resource.command(cmd) + end + # Include env vars as needed. + resource.environment.update(new_resource.parent_ruby.ruby_environment) if new_resource.parent_ruby + resource.environment['BUNDLE_GEMFILE'] = new_resource.parent_bundle.gemfile_path if new_resource.parent_bundle + end + + end + end +end diff --git a/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/version.rb b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/version.rb new file mode 100644 index 0000000..48f7d53 --- /dev/null +++ b/cookbooks/application_ruby/files/halite_gem/poise_application_ruby/version.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module PoiseApplicationRuby + VERSION = '4.0.1' +end diff --git a/cookbooks/application_ruby/libraries/default.rb b/cookbooks/application_ruby/libraries/default.rb new file mode 100644 index 0000000..673a63b --- /dev/null +++ b/cookbooks/application_ruby/libraries/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) +require "poise_application_ruby/cheftie" diff --git a/cookbooks/application_ruby/metadata.json b/cookbooks/application_ruby/metadata.json new file mode 100644 index 0000000..3707047 --- /dev/null +++ b/cookbooks/application_ruby/metadata.json @@ -0,0 +1 @@ +{"name":"application_ruby","version":"4.0.1","description":"A Chef cookbook for deploying application code.","long_description":"# Application_Ruby Cookbook\n\n[![Build Status](https://img.shields.io/travis/poise/application_ruby.svg)](https://travis-ci.org/poise/application_ruby)\n[![Gem Version](https://img.shields.io/gem/v/poise-application-ruby.svg)](https://rubygems.org/gems/poise-application-ruby)\n[![Cookbook Version](https://img.shields.io/cookbook/v/application_ruby.svg)](https://supermarket.chef.io/cookbooks/application_ruby)\n[![Coverage](https://img.shields.io/codecov/c/github/poise/application_ruby.svg)](https://codecov.io/github/poise/application_ruby)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/application_ruby.svg)](https://gemnasium.com/poise/application_ruby)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nA [Chef](https://www.chef.io/) cookbook to deploy Ruby applications.\n\n## Quick Start\n\nTo deploy a Rails application from git:\n\n```ruby\napplication '/srv/myapp' do\n git 'https://github.com/example/myapp.git'\n bundle_install do\n deployment true\n without %w{development test}\n end\n rails do\n database 'sqlite3:///db.sqlite3'\n secret_token 'd78fe08df56c9'\n migrate true\n end\n unicorn do\n port 8000\n end\nend\n```\n\n## Requirements\n\nChef 12 or newer is required.\n\n## Resources\n\n### `application_bundle_install`\n\nThe `application_bundle_install` resource installs gems using Bundler for a\ndeployment.\n\n```ruby\napplication '/srv/myapp' do\n bundle_install do\n deployment true\n without %w{development test}\n end\nend\n```\n\nAll actions and properties are the same as the [`bundle_install` resource](https://github.com/poise/poise-ruby#bundle_install).\n\n### `application_rackup`\n\nThe `application_rackup` resource creates a service for `rackup`.\n\n```ruby\napplication '/srv/myapp' do\n rackup do\n port 8000\n end\nend\n```\n\n#### Actions\n\n* `:enable` – Create, enable and start the service. *(default)*\n* `:disable` – Stop, disable, and destroy the service.\n* `:start` – Start the service.\n* `:stop` – Stop the service.\n* `:restart` – Stop and then start the service.\n* `:reload` – Send the configured reload signal to the service.\n\n#### Properties\n\n* `path` – Base path for the application. *(name attribute)*\n* `port` – Port to listen on. *(default: 80)*\n* `service_name` – Name of the service to create. *(default: auto-detect)*\n# `user` – User to run the service as. *(default: application owner)*\n\n### `application_rails`\n\nThe `application_rails` resource\n\n```ruby\napplication '/srv/myapp' do\n rails do\n database 'sqlite3:///db.sqlite3'\n secret_token 'd78fe08df56c9'\n migrate true\n end\nend\n```\n\n#### Actions\n\n* `:deploy` – Create config files and run required deployments steps. *(default)*\n\n#### Properties\n\n* `path` – Base path for the application. *(name attribute)*\n* `database` – Database settings for Rails. See [the database section\n below](#database-parameters) for more information. *(option collector)*\n* `migrate` – Run database migrations. *(default: false)*\n* `precompile_assets` – Run `rake assets:precompile`. *(default: auto-detect)()\n* `rails_env` – Rails environment name. *(default: node.chef_environment)*\n* `secret_token` – Secret token for Rails session verification et al.\n* `secrets_mode` – Secrets configuration mode. Set to `:yaml` to generate a\n Rails 4.2 secrets.yml. Set to `:initializer` to update\n `config/initializers/secret_token.rb`. *(default: auto-detect)*\n\n**NOTE:** At this time `secrets_mode :initializer` is not implemented.\n\n#### Database Parameters\n\nThe database parameters can be set in three ways: URL, hash, and block.\n\nIf you have a single URL for the parameters, you can pass it directly to\n`database`:\n\n```ruby\nrails do\n database 'mysql2://myuser@dbhost/myapp'\nend\n```\n\nPassing a single URL will also set the `$DATABASE_URL` environment variable\nautomatically for compatibility with Heroku-based applications.\n\nAs with other option collector resources, you can pass individual settings as\neither a hash or block:\n\n```ruby\nrails do\n database do\n adapter 'mysql2'\n username 'myuser'\n host 'dbhost'\n database 'myapp'\n end\nend\n\nrails do\n database({\n adapter: 'mysql2',\n username: 'myuser',\n host: 'dbhost',\n database: 'myapp',\n })\nend\n```\n\n### `application_ruby`\n\nThe `application_ruby` resource installs a Ruby runtime for the deployment.\n\n```ruby\napplication '/srv/myapp' do\n ruby '2.2'\nend\n```\n\nAll actions and properties are the same as the [`ruby_runtime` resource](https://github.com/poise/poise-ruby#ruby_runtime).\n\n### `application_ruby_gem`\n\nThe `application_ruby_gem` resource installs Ruby gems for the deployment.\n\n```ruby\napplication '/srv/myapp' do\n ruby_gem 'rake'\nend\n```\n\nAll actions and properties are the same as the [`ruby_gem` resource](https://github.com/poise/poise-ruby#ruby_gem).\n\n### `application_ruby_execute`\n\nThe `application_ruby_execute` resource runs Ruby commands for the deployment.\n\n```ruby\napplication '/srv/myapp' do\n ruby_execute 'rake'\nend\n```\n\nAll actions and properties are the same as the [`ruby_execute` resource](https://github.com/poise/poise-ruby#ruby_execute),\nexcept that the `cwd`, `environment`, `group`, and `user` properties default to\nthe application-level data if not specified.\n\n### `application_thin`\n\nThe `application_thin` resource creates a service for `thin`.\n\n```ruby\napplication '/srv/myapp' do\n thin do\n port 8000\n end\nend\n```\n\n#### Actions\n\n* `:enable` – Create, enable and start the service. *(default)*\n* `:disable` – Stop, disable, and destroy the service.\n* `:start` – Start the service.\n* `:stop` – Stop the service.\n* `:restart` – Stop and then start the service.\n* `:reload` – Send the configured reload signal to the service.\n\n#### Properties\n\n* `path` – Base path for the application. *(name attribute)*\n* `config_path` – Path to a Thin configuration file.\n* `port` – Port to listen on. *(default: 80)*\n* `service_name` – Name of the service to create. *(default: auto-detect)*\n# `user` – User to run the service as. *(default: application owner)*\n\n### `application_unicorn`\n\nThe `application_unicorn` resource creates a service for `unicorn`.\n\n```ruby\napplication '/srv/myapp' do\n unicorn do\n port 8000\n end\nend\n```\n\n#### Actions\n\n* `:enable` – Create, enable and start the service. *(default)*\n* `:disable` – Stop, disable, and destroy the service.\n* `:start` – Start the service.\n* `:stop` – Stop the service.\n* `:restart` – Stop and then start the service.\n* `:reload` – Send the configured reload signal to the service.\n\n#### Properties\n\n* `path` – Base path for the application. *(name attribute)*\n* `port` – Port to listen on. *(default: 80)*\n* `service_name` – Name of the service to create. *(default: auto-detect)*\n# `user` – User to run the service as. *(default: application owner)*\n\n## Sponsors\n\nDevelopment sponsored by [Chef Software](https://www.chef.io/), [Symonds & Son](http://symondsandson.com/), and [Orion](https://www.orionlabs.co/).\n\nThe Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/).\n\n## License\n\nCopyright 2015, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainer":"YOUR_COMPANY_NAME","maintainer_email":"YOUR_EMAIL","license":"none","platforms":{},"dependencies":{"poise":"~> 2.0","application":"~> 5.0","poise-ruby":"~> 2.1","poise-service":"~> 1.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{}} \ No newline at end of file diff --git a/cookbooks/application_ruby/templates/database.yml.erb b/cookbooks/application_ruby/templates/database.yml.erb new file mode 100644 index 0000000..a1bdc6e --- /dev/null +++ b/cookbooks/application_ruby/templates/database.yml.erb @@ -0,0 +1,3 @@ +# Generated by Chef for <%= @new_resource.to_s %> + +<%= @config.to_yaml %> diff --git a/cookbooks/application_ruby/templates/secrets.yml.erb b/cookbooks/application_ruby/templates/secrets.yml.erb new file mode 100644 index 0000000..a1bdc6e --- /dev/null +++ b/cookbooks/application_ruby/templates/secrets.yml.erb @@ -0,0 +1,3 @@ +# Generated by Chef for <%= @new_resource.to_s %> + +<%= @config.to_yaml %> diff --git a/cookbooks/dmg/.foodcritic b/cookbooks/dmg/.foodcritic new file mode 100644 index 0000000..3318b2a --- /dev/null +++ b/cookbooks/dmg/.foodcritic @@ -0,0 +1,4 @@ +~FC007 +~FC023 +~FC024 +~FC048 diff --git a/cookbooks/dmg/CHANGELOG.md b/cookbooks/dmg/CHANGELOG.md new file mode 100644 index 0000000..9eed8f2 --- /dev/null +++ b/cookbooks/dmg/CHANGELOG.md @@ -0,0 +1,113 @@ +# dmg Cookbook CHANGELOG + +This file is used to list changes made in each version of the dmg cookbook. + +## 3.1.0 (2017-01-18) + +- Fixed pkg,mpkg installation when it was using mounted app name while it was actually mounted under different name for some applications +- Cookstyle fixes + +## 3.0.0 (2016-09-06) + +- Add chef_version metadata +- Run the specs against a mock of OS X +- Testing updates +- Require Chef 12+ + +## v2.4.0 (2016-04-26) + +- Added support for local .dmg files with the file property. See the readme for details +- Resolved all rubocop warnings + +## v2.3.0 (2015-10-20) + +- Add new headers property to the LWRP for custom http headers. See the readme for more information +- Removed pivotal tracker example in the readme +- Added travis and cookbook version badges to the readme +- Added a .foodcritic file to exclude rules +- Updated chefignore and .gitignore files +- Updated platforms in Test Kitchen +- Added standard Rubocop file +- Updated Travis to test using ChefDK for the latest deps +- Added a Berksfile +- Updated contributing and testing docs +- Updated Gemfile with the latest testing deps +- Added maintainers.md and maintainers.toml +- Added rakefile for simplified testing +- Added source_url and issues_url metadata +- Added basic converge chefspec + +## v2.2.2 (2014-11-12) + +- # 23, add chefspec matchers + +## v2.2.0 (2014-02-25) + +- [COOK-4285] Accept long EULAs + +## v2.1.4 (2014-01-26) + +- [COOK-4157] - dmg_package LWRP broken due to "puts" instead of "system" +- [COOK-4065] - dmg cookbook outputs the name of packages when checking if they are installed + +## v2.1.2 + +Cleaning up merge errors + +## v2.1.0 + +### Bug + +- **[COOK-3946](https://tickets.chef.io/browse/COOK-3946)** - Syntax error in resources/package.rb +- **[COOK-2672](https://tickets.chef.io/browse/COOK-2672)** - EULA for package is displayed instead accepted + +## v2.0.8 + +Adding a Chef 10 compatibility check in provider + +## v2.0.6 + +# BUG + +- [COOK-3302] - Sometimes hdiutil detach fails due to cfprefsd running in background + + # IMPROVEMENT + +- Adding foodcritic and rubocop to .travis.yml + +## v2.0.4 + +### Bug + +- **[COOK-3331](https://tickets.chef.io/browse/COOK-3331)** - Fix an issue where `dmg_package` with no source raises an exception + +## v2.0.2 + +### Bug + +- **[COOK-3578](https://tickets.chef.io/browse/COOK-3578)** - Support `package_id`s with spaces +- **[COOK-3302](https://tickets.chef.io/browse/COOK-3302)** - Fix an issue where `hdiutil detach` fails due to `cfprefsd` running in the background + +## v2.0.0 + +### Bug + +- **[COOK-3389](https://tickets.chef.io/browse/COOK-3389)** - Use `rsync` instead of `cp` (potentially a breaking change on some systems) + +## v1.1.0 + +- [COOK-1847] - accept owner parameter for installing packages + +## v1.0.0 + +- [COOK-852] - Support "pkg" in addition to "mpkg" package types + +## v0.7.0 + +- [COOK-854] - use `cp -R` instead of `cp -r` +- [COOK-855] - specify a file or directory to check for prior install + +## v0.6.0 + +- option to install software that is an .mpkg inside a .dmg +- ignore failure on chmod in case mode is already set, or is root owned diff --git a/cookbooks/dmg/CONTRIBUTING.md b/cookbooks/dmg/CONTRIBUTING.md new file mode 100644 index 0000000..ef2f2b8 --- /dev/null +++ b/cookbooks/dmg/CONTRIBUTING.md @@ -0,0 +1,2 @@ +Please refer to +https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/CONTRIBUTING.MD diff --git a/cookbooks/dmg/MAINTAINERS.md b/cookbooks/dmg/MAINTAINERS.md new file mode 100644 index 0000000..c8f99e2 --- /dev/null +++ b/cookbooks/dmg/MAINTAINERS.md @@ -0,0 +1,18 @@ + + +# Maintainers +This file lists how this cookbook project is maintained. When making changes to the system, this +file tells you who needs to review your patch - you need a review from an existing maintainer +for the cookbook to provide a :+1: on your pull request. Additionally, you need +to not receive a veto from a Lieutenant or the Project Lead. + +Check out [How Cookbooks are Maintained](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/CONTRIBUTING.MD) +for details on the process and how to become a maintainer or the project lead. + +# Project Maintainer +* [Tim Smith](https://github.com/tas50) + +# Maintainers +* [Jennifer Davis](https://github.com/sigje) +* [Tim Smith](https://github.com/tas50) +* [Thom May](https://github.com/thommay) diff --git a/cookbooks/dmg/README.md b/cookbooks/dmg/README.md new file mode 100644 index 0000000..7cbbb13 --- /dev/null +++ b/cookbooks/dmg/README.md @@ -0,0 +1,146 @@ +# dmg Cookbook + +[![Build Status](https://travis-ci.org/chef-cookbooks/dmg.svg?branch=master)](https://travis-ci.org/chef-cookbooks/dmg) [![Cookbook Version](https://img.shields.io/cookbook/v/dmg.svg)](https://supermarket.chef.io/cookbooks/dmg) + +Resource to install OS X applications (.app) from dmg files. + +## Requirements + +### Platforms + +- Mac OS X + +### Chef + +- Chef 12.1+ + +### Cookbooks + +- none + +## Resources/Providers + +### dmg_package + +This resource will install a DMG "Package". It will retrieve the DMG from a remote URL, mount it using OS X's `hdid`, copy the application (.app directory) to the specified destination (/Applications), and detach the image using `hdiutil`. The dmg file will be stored in the `Chef::Config[:file_cache_path]`. If you want to install an application that has already been downloaded (not using the `source` parameter), copy it to the appropriate location. You can find out what directory this is with the following command on the node to run chef: + +```bash +knife exec -E 'p Chef::Config[:file_cache_path]' -c /etc/chef/client.rb +``` + +Optionally, the LWRP can install an "mpkg" or "pkg" package using installer(8). + +#### Actions + +- :install - Installs the application. + +#### Parameter attributes: + +- `app` - This is the name of the application used by default for the /Volumes directory and the .app directory copied to /Applications. +- `source` - remote URL for the dmg to download if specified. Default is nil. +- `file` - local dmg full file path. Default is nil. +- `owner` - owner that should own the package installation. +- `destination` - directory to copy the .app into. Default is /Applications. +- `checksum` - sha256 checksum of the dmg to download. Default is nil. +- `type` - type of package, "app", "pkg" or "mpkg". Default is "app". When using "pkg" or "mpkg", the destination must be /Applications. +- `volumes_dir` - Directory under /Volumes where the dmg is mounted. Not all dmgs are mounted into a /Volumes location matching the name of the dmg. If not specified, this will use the name attribute. +- `package_id` - Package id registered with pkgutil when a pkg or mpkg is installed +- `dmg_name` - Specify the name of the dmg if it is not the same as `app`, or if the name has spaces. +- `dmg_passphrase` - Specify a passphrase to use to unencrypt the dmg while mounting. +- `accept_eula` - Specify whether to accept the EULA. Certain dmgs require acceptance of EULA before mounting. Can be true or false, defaults to false. +- `headers` - Allows custom HTTP headers (like cookies) to be set on the remote_file resource. + +#### Examples + +Install `/Applications/Tunnelblick.app` from the primary download site. + +```ruby +dmg_package 'Tunnelblick' do + source 'http://tunnelblick.googlecode.com/files/Tunnelblick_3.1.2.dmg' + checksum 'a3fae60b6833175f32df20c90cd3a3603a' + action :install +end +``` + +Install Google Chrome. Uses the `dmg_name` because the application name has spaces. Installs in `/Applications/Google Chrome.app`. + +```ruby +dmg_package 'Google Chrome' do + dmg_name 'googlechrome' + source 'https://dl-ssl.google.com/chrome/mac/stable/GGRM/googlechrome.dmg' + checksum '7daa2dc5c46d9bfb14f1d7ff4b33884325e5e63e694810adc58f14795165c91a' + action :install +end +``` + +Install Dropbox. Uses `volumes_dir` because the mounted directory is different than the name of the application directory. Installs in `/Applications/Dropbox.app`. + +```ruby +dmg_package 'Dropbox' do + volumes_dir 'Dropbox Installer' + source 'http://www.dropbox.com/download?plat=mac' + checksum 'b4ea620ca22b0517b75753283ceb82326aca8bc3c86212fbf725de6446a96a13' + action :install +end +``` + +Install MacIrssi to `~/Applications` from the local file downloaded to the cache path into an Applications directory in the current user's home directory. Chef should run as a non-root user for this. + +```ruby +directory "#{ENV['HOME']}/Applications" + +dmg_package 'MacIrssi' do + destination "#{ENV['HOME']}/Applications" + action :install +end +``` + +Install Virtualbox to `/Applications` from the .mpkg: + +```ruby +dmg_package 'Virtualbox' do + source 'http://dlc.sun.com.edgesuite.net/virtualbox/4.0.8/VirtualBox-4.0.8-71778-OSX.dmg' + type 'mpkg' +end +``` + +Install pgAdmin to `/Applications` and automatically accept the EULA: + +```ruby +dmg_package 'pgAdmin3' do + source 'http://wwwmaster.postgresql.org/redir/198/h/pgadmin3/release/v1.12.3/osx/pgadmin3-1.12.3.dmg' + checksum '9435f79d5b52d0febeddfad392adf82db9df159196f496c1ab139a6957242ce9' + accept_eula true +end +``` + +Install Silverlight, with idempotence check based on pkgutil: + +```ruby +dmg_package 'Silerlight' do + source 'http://silverlight.dlservice.microsoft.com/download/D/C/2/DC2D5838-9138-4D25-AA92-52F61F7C51E6/runtime/Silverlight.dmg' + type 'pkg' + checksum '6d4a0ad4552d9815531463eb3f467fb8cf4bffcc' + package_id 'com.microsoft.installSilverlightPlugin' +end +``` + +## License & Authors + +**Author:** Cookbook Engineering Team ([cookbooks@chef.io](mailto:cookbooks@chef.io)) + +**Copyright:** 2011-2015, Chef Software, Inc. + +``` +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/cookbooks/dmg/attributes/default.rb b/cookbooks/dmg/attributes/default.rb new file mode 100644 index 0000000..e8cbfae --- /dev/null +++ b/cookbooks/dmg/attributes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook:: dmg +# Attributes:: default +# +# Copyright:: 2011-2016, Chef Software, Inc. +# +# 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. +# +default['dmg']['base_dir'] = '/Applications' +default['dmg']['cache_dir'] = Chef::Config[:file_cache_path] diff --git a/cookbooks/postgresql/files/default/tests/minitest/default_test.rb b/cookbooks/dmg/libraries/matchers.rb similarity index 64% rename from cookbooks/postgresql/files/default/tests/minitest/default_test.rb rename to cookbooks/dmg/libraries/matchers.rb index 8acbabf..5b688c2 100644 --- a/cookbooks/postgresql/files/default/tests/minitest/default_test.rb +++ b/cookbooks/dmg/libraries/matchers.rb @@ -1,5 +1,8 @@ # -# Copyright 2012, Opscode, Inc. +# Cookbook:: dmg +# Library:: matchers +# +# Copyright:: 2014-2016, Fletcher Nichol # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,14 +17,8 @@ # limitations under the License. # -require File.expand_path('../support/helpers', __FILE__) - -describe 'postgresql::default' do - include Helpers::Postgresql - - it 'installs the postgresql client packages' do - node['postgresql']['client']['packages'].each do |pkg| - package(pkg).must_be_installed - end +if defined?(ChefSpec) + def install_dmg_package(app) + ChefSpec::Matchers::ResourceMatcher.new(:dmg_package, :install, app) end end diff --git a/cookbooks/dmg/metadata.json b/cookbooks/dmg/metadata.json new file mode 100644 index 0000000..6e9e1e1 --- /dev/null +++ b/cookbooks/dmg/metadata.json @@ -0,0 +1 @@ +{"name":"dmg","version":"3.1.0","description":"LWRP to install OS X applications from dmgs","long_description":"# dmg Cookbook\n\n[![Build Status](https://travis-ci.org/chef-cookbooks/dmg.svg?branch=master)](https://travis-ci.org/chef-cookbooks/dmg) [![Cookbook Version](https://img.shields.io/cookbook/v/dmg.svg)](https://supermarket.chef.io/cookbooks/dmg)\n\nResource to install OS X applications (.app) from dmg files.\n\n## Requirements\n\n### Platforms\n\n- Mac OS X\n\n### Chef\n\n- Chef 12.1+\n\n### Cookbooks\n\n- none\n\n## Resources/Providers\n\n### dmg_package\n\nThis resource will install a DMG \"Package\". It will retrieve the DMG from a remote URL, mount it using OS X's `hdid`, copy the application (.app directory) to the specified destination (/Applications), and detach the image using `hdiutil`. The dmg file will be stored in the `Chef::Config[:file_cache_path]`. If you want to install an application that has already been downloaded (not using the `source` parameter), copy it to the appropriate location. You can find out what directory this is with the following command on the node to run chef:\n\n```bash\nknife exec -E 'p Chef::Config[:file_cache_path]' -c /etc/chef/client.rb\n```\n\nOptionally, the LWRP can install an \"mpkg\" or \"pkg\" package using installer(8).\n\n#### Actions\n\n- :install - Installs the application.\n\n#### Parameter attributes:\n\n- `app` - This is the name of the application used by default for the /Volumes directory and the .app directory copied to /Applications.\n- `source` - remote URL for the dmg to download if specified. Default is nil.\n- `file` - local dmg full file path. Default is nil.\n- `owner` - owner that should own the package installation.\n- `destination` - directory to copy the .app into. Default is /Applications.\n- `checksum` - sha256 checksum of the dmg to download. Default is nil.\n- `type` - type of package, \"app\", \"pkg\" or \"mpkg\". Default is \"app\". When using \"pkg\" or \"mpkg\", the destination must be /Applications.\n- `volumes_dir` - Directory under /Volumes where the dmg is mounted. Not all dmgs are mounted into a /Volumes location matching the name of the dmg. If not specified, this will use the name attribute.\n- `package_id` - Package id registered with pkgutil when a pkg or mpkg is installed\n- `dmg_name` - Specify the name of the dmg if it is not the same as `app`, or if the name has spaces.\n- `dmg_passphrase` - Specify a passphrase to use to unencrypt the dmg while mounting.\n- `accept_eula` - Specify whether to accept the EULA. Certain dmgs require acceptance of EULA before mounting. Can be true or false, defaults to false.\n- `headers` - Allows custom HTTP headers (like cookies) to be set on the remote_file resource.\n\n#### Examples\n\nInstall `/Applications/Tunnelblick.app` from the primary download site.\n\n```ruby\ndmg_package 'Tunnelblick' do\n source 'http://tunnelblick.googlecode.com/files/Tunnelblick_3.1.2.dmg'\n checksum 'a3fae60b6833175f32df20c90cd3a3603a'\n action :install\nend\n```\n\nInstall Google Chrome. Uses the `dmg_name` because the application name has spaces. Installs in `/Applications/Google Chrome.app`.\n\n```ruby\ndmg_package 'Google Chrome' do\n dmg_name 'googlechrome'\n source 'https://dl-ssl.google.com/chrome/mac/stable/GGRM/googlechrome.dmg'\n checksum '7daa2dc5c46d9bfb14f1d7ff4b33884325e5e63e694810adc58f14795165c91a'\n action :install\nend\n```\n\nInstall Dropbox. Uses `volumes_dir` because the mounted directory is different than the name of the application directory. Installs in `/Applications/Dropbox.app`.\n\n```ruby\ndmg_package 'Dropbox' do\n volumes_dir 'Dropbox Installer'\n source 'http://www.dropbox.com/download?plat=mac'\n checksum 'b4ea620ca22b0517b75753283ceb82326aca8bc3c86212fbf725de6446a96a13'\n action :install\nend\n```\n\nInstall MacIrssi to `~/Applications` from the local file downloaded to the cache path into an Applications directory in the current user's home directory. Chef should run as a non-root user for this.\n\n```ruby\ndirectory \"#{ENV['HOME']}/Applications\"\n\ndmg_package 'MacIrssi' do\n destination \"#{ENV['HOME']}/Applications\"\n action :install\nend\n```\n\nInstall Virtualbox to `/Applications` from the .mpkg:\n\n```ruby\ndmg_package 'Virtualbox' do\n source 'http://dlc.sun.com.edgesuite.net/virtualbox/4.0.8/VirtualBox-4.0.8-71778-OSX.dmg'\n type 'mpkg'\nend\n```\n\nInstall pgAdmin to `/Applications` and automatically accept the EULA:\n\n```ruby\ndmg_package 'pgAdmin3' do\n source 'http://wwwmaster.postgresql.org/redir/198/h/pgadmin3/release/v1.12.3/osx/pgadmin3-1.12.3.dmg'\n checksum '9435f79d5b52d0febeddfad392adf82db9df159196f496c1ab139a6957242ce9'\n accept_eula true\nend\n```\n\nInstall Silverlight, with idempotence check based on pkgutil:\n\n```ruby\ndmg_package 'Silerlight' do\n source 'http://silverlight.dlservice.microsoft.com/download/D/C/2/DC2D5838-9138-4D25-AA92-52F61F7C51E6/runtime/Silverlight.dmg'\n type 'pkg'\n checksum '6d4a0ad4552d9815531463eb3f467fb8cf4bffcc'\n package_id 'com.microsoft.installSilverlightPlugin'\nend\n```\n\n## License & Authors\n\n**Author:** Cookbook Engineering Team ([cookbooks@chef.io](mailto:cookbooks@chef.io))\n\n**Copyright:** 2011-2015, Chef Software, Inc.\n\n```\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","maintainer":"Chef Software, Inc","maintainer_email":"cookbooks@chef.io","license":"Apache 2.0","platforms":{"mac_os_x":">= 0.0.0"},"dependencies":{},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{}} \ No newline at end of file diff --git a/cookbooks/dmg/providers/package.rb b/cookbooks/dmg/providers/package.rb new file mode 100644 index 0000000..ac0bb9a --- /dev/null +++ b/cookbooks/dmg/providers/package.rb @@ -0,0 +1,95 @@ +# +# Cookbook:: dmg +# Provider:: package +# +# Copyright:: 2011-2016, Chef Software, Inc. +# +# 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. +# + +include Chef::Mixin::ShellOut + +use_inline_resources if defined?(use_inline_resources) + +def load_current_resource + @dmgpkg = Chef::Resource::DmgPackage.new(new_resource.name) + @dmgpkg.app(new_resource.app) + Chef::Log.debug("Checking for application #{new_resource.app}") + @dmgpkg.installed(installed?) +end + +action :install do + unless @dmgpkg.installed + + volumes_dir = new_resource.volumes_dir ? new_resource.volumes_dir : new_resource.app + dmg_name = new_resource.dmg_name ? new_resource.dmg_name : new_resource.app + + dmg_file = if new_resource.file.nil? + "#{Chef::Config[:file_cache_path]}/#{dmg_name}.dmg" + else + new_resource.file + end + + remote_file "#{dmg_file} - #{@dmgpkg.name}" do + path dmg_file + source new_resource.source + headers new_resource.headers if new_resource.headers + checksum new_resource.checksum if new_resource.checksum + end if new_resource.source + + passphrase_cmd = new_resource.dmg_passphrase ? "-passphrase #{new_resource.dmg_passphrase}" : '' + ruby_block "attach #{dmg_file}" do + block do + cmd = shell_out("hdiutil imageinfo #{passphrase_cmd} '#{dmg_file}' | grep -q 'Software License Agreement: true'") + software_license_agreement = cmd.exitstatus.zero? + raise "Requires EULA Acceptance; add 'accept_eula true' to package resource" if software_license_agreement && !new_resource.accept_eula + accept_eula_cmd = new_resource.accept_eula ? 'echo Y | PAGER=true' : '' + shell_out!("#{accept_eula_cmd} hdiutil attach #{passphrase_cmd} '#{dmg_file}' -mountpoint '/Volumes/#{volumes_dir}' -quiet") + end + not_if "hdiutil info #{passphrase_cmd} | grep -q 'image-path.*#{dmg_file}'" + end + + case new_resource.type + when 'app' + execute "rsync --force --recursive --links --perms --executability --owner --group --times '/Volumes/#{volumes_dir}/#{new_resource.app}.app' '#{new_resource.destination}'" do + user new_resource.owner if new_resource.owner + end + + file "#{new_resource.destination}/#{new_resource.app}.app/Contents/MacOS/#{new_resource.app}" do + mode '755' + ignore_failure true + end + when 'mpkg', 'pkg' + execute "installation_file=$(ls '/Volumes/#{volumes_dir}' | grep '.#{new_resource.type}$') && sudo installer -pkg \"/Volumes/#{volumes_dir}/$installation_file\" -target /" do + # Prevent cfprefsd from holding up hdiutil detach for certain disk images + environment('__CFPREFERENCES_AVOID_DAEMON' => '1') if Gem::Version.new(node['platform_version']) >= Gem::Version.new('10.8') + end + end + + execute "hdiutil detach '/Volumes/#{volumes_dir}' || hdiutil detach '/Volumes/#{volumes_dir}' -force" + end +end + +private + +def installed? + if ::File.directory?("#{new_resource.destination}/#{new_resource.app}.app") + Chef::Log.info "Already installed; to upgrade, remove \"#{new_resource.destination}/#{new_resource.app}.app\"" + true + elsif shell_out("pkgutil --pkgs='#{new_resource.package_id}'").exitstatus.zero? + Chef::Log.info "Already installed; to upgrade, try \"sudo pkgutil --forget '#{new_resource.package_id}'\"" + true + else + false + end +end diff --git a/cookbooks/application/recipes/default.rb b/cookbooks/dmg/recipes/default.rb similarity index 80% rename from cookbooks/application/recipes/default.rb rename to cookbooks/dmg/recipes/default.rb index ec67eb6..017b9d8 100644 --- a/cookbooks/application/recipes/default.rb +++ b/cookbooks/dmg/recipes/default.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: application +# Cookbook Name:: dmg # Recipe:: default # -# Copyright:: 2012, Opscode, Inc +# Copyright 2011-2016, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,4 +16,3 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# Empty placeholder recipe, use the LWRPs, see README.md. diff --git a/cookbooks/dmg/resources/package.rb b/cookbooks/dmg/resources/package.rb new file mode 100644 index 0000000..4d1a2d8 --- /dev/null +++ b/cookbooks/dmg/resources/package.rb @@ -0,0 +1,39 @@ +# Encoding: utf-8 +# Cookbook:: dmg +# Resource:: package +# +# Copyright:: 2011-2016, Joshua Timberman +# +# 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. +# +actions :install + +attribute :app, kind_of: String, name_attribute: true +attribute :source, kind_of: String, default: nil +attribute :file, kind_of: String, default: nil +attribute :owner, kind_of: String, default: nil +attribute :destination, kind_of: String, default: '/Applications' +attribute :checksum, kind_of: String, default: nil +attribute :volumes_dir, kind_of: String, default: nil +attribute :dmg_name, kind_of: String, default: nil +attribute :type, kind_of: String, default: 'app' +attribute :installed, kind_of: [TrueClass, FalseClass], default: false +attribute :package_id, kind_of: String, default: nil +attribute :dmg_passphrase, kind_of: String, default: nil +attribute :accept_eula, kind_of: [TrueClass, FalseClass], default: false +attribute :headers, kind_of: Hash, default: nil + +def initialize(name, run_context = nil) + super + @action = :install +end diff --git a/cookbooks/git/.foodcritic b/cookbooks/git/.foodcritic new file mode 100644 index 0000000..b9f8767 --- /dev/null +++ b/cookbooks/git/.foodcritic @@ -0,0 +1 @@ +~FC016 diff --git a/cookbooks/git/CHANGELOG.md b/cookbooks/git/CHANGELOG.md new file mode 100644 index 0000000..d2f2797 --- /dev/null +++ b/cookbooks/git/CHANGELOG.md @@ -0,0 +1,257 @@ +# git Cookbook CHANGELOG + +This file is used to list changes made in each version of the git cookbook. + +## 6.0.0 (2017-02-14) + +- Fail on deprecations is now enabled so we're fully Chef 13 compatible +- Define the chefspec matchers properly +- Remove the legacy platform mappings that fail on Chef 13 +- Improve the test cookbook / integration tests +- Convert config LWRP to a custom resource and make it fully idempotent +- Require Chef 12.5 or later + +## 5.0.2 (2017-01-18) + +- Remove arch for the metadata +- Avoid deprecation warning during testing +- respond_to?(:chef_version) for < 12.6 compat + +## 5.0.1 (2016-09-15) + +- Clarify we require Chef 12.1 or later + +## 5.0.0 (2016-09-02) + +- Require Chef 12 or later +- Don't depend on the windows cookbook since windows_package is built into Chef 12 +- Updates for testing + +## v4.6.0 (2016-07-05) + +- Added support for compiling git on suse +- Added the ability to pass a new group property to the config provider +- Documented the git_config provider +- Added the tar package on RHEL/Fedora for source installs as some minimal installs lack this package +- Added suse, opensuse, and opensuseleap as supported platforms in the metadata +- Switched to inspec for testing +- Switched to cookstyle for Ruby linting +- Added Travis integration testing of Debian 7/8 + +## v4.5.0 (2016-04-28) + +- Update git versions to 2.8.1 + +## v4.4.1 (2016-03-31) + +- PR #95 support 32 bit and 64 bit installs on windows @smurawski + +## v4.4.0 (2016-03-23) + +- PR #93 bump to latest git @ksubrama + +## v4.3.7 (2016-02-03) + +- PR #90 port node[git][server][export_all] to true/false @scalp42 +- PR #89 make attributes more wrapper friendly @scalp42 +- Update testing deps + rubocop fixes +- README fix @zverulacis + +## v4.3.6 (2016-01-25) + +- Windows fixes + +## v4.3.5 (2015-12-15) + +- Fixed installation on Windows nodes +- Removed the last of the Chef 10 compatibility code +- Added up to date contributing and testing docs +- Updated test deps in the Gemfile +- Removed test kitchen digital ocean config +- Test with kitchen-docker in Travis CI +- Removed uncessary windows cookbook entry from the Berksfile +- Added the chef standard rubocop.yml file and resolved all warnings +- Added chefignore file +- Removed bin dir +- Added maintainers.md and maintainers.toml files +- Added travis and supermarket version badges to the readme + +## v4.3.4 (2015-09-06) + +- Fixing package_id on OSX +- Adding 2.5.1 data for Windows + +## v4.3.3 (2015-07-27) + +- # 76: Use checksum keyname instead of value in source recipe + +## v4.3.2 (2015-07-27) + +- Fixing up Windows provider (issue #73) +- Supporting changes to source_prefix in source provider (#62) + +## v4.3.1 (2015-07-23) + +- Fixing up osx_dmg_source_url + +## v4.3.0 (2015-07-20) + +- Removing references to node attributes from provider code +- Name-spacing of client resource property names +- Addition of windows recipe +- Creation of package recipe + +## v4.2.4 (2015-07-19) + +- Fixing source provider selection bug from 4.2.3 + +## v4.2.3 (2015-07-18) + +- mac_os_x provider mapping +- various rubocops + +## v4.2.2 (2015-04-23) + +- Fix up action in Chef::Resource::GitService +- Adding matchers + +## v4.2.1 (2015-04-17) + +- Fixing Chef 11 support. +- Adding provider mapping file + +## v4.2.0 (2015-04-15) + +- Converting recipes to resources. +- Keeping recipe interface for backwards compat + +## v4.1.0 (2014-12-23) + +- Fixing windows package checksums +- Various test coverage additions + +## v4.0.2 (2014-04-23) + +- [COOK-4482] - Add FreeBSD support for installing git client + +## v4.0.0 (2014-03-18) + +- [COOK-4397] Only use_inline_resources on Chef 11 + +## v3.1.0 (2014-03-12) + +- [COOK-4392] - Cleanup git_config LWRP + +## v3.0.0 (2014-02-28) + +[COOK-4387] Add git_config type [COOK-4388] Fix up rubocops [COOK-4390] Add integration tests for default and server suites + +## v2.10.0 (2014-02-25) + +- [COOK-4146] - wrong dependency in git::source for rhel 6 +- [COOK-3947] - Git cookbook adds itself to the path every run + +## v2.9.0 + +Updating to depend on cookbook yum ~> 3 Fixing style to pass rubocop Updating test scaffolding + +## v2.8.4 + +fixing metadata version error. locking to 3.0 + +## v2.8.1 + +Locking yum dependency to '< 3' + +## v2.8.0 + +### Bug + +- [COOK-3433] - git::server does not correctly set git-daemon's base-path on Debian + +## v2.7.0 + +### Bug + +- **[COOK-3624](https://tickets.chef.io/browse/COOK-3624)** - Don't restart `xinetd` on each Chef client run +- **[COOK-3482](https://tickets.chef.io/browse/COOK-3482)** - Force git to add itself to the current process' PATH + +### New Feature + +- **[COOK-3223](https://tickets.chef.io/browse/COOK-3223)** - Support Omnios and SmartOS package installs + +## v2.6.0 + +### Improvement + +- **[COOK-3193](https://tickets.chef.io/browse/COOK-3193)** - Add proper debian packages + +## v2.5.2 + +### Bug + +- [COOK-2813]: Fix bad string interpolation in source recipe + +## v2.5.0 + +- Relax runit version constraint (now depend on 1.0+). + +## v2.4.0 + +- [COOK-2734] - update git versions + +## v2.3.0 + +- [COOK-2385] - update git::server for `runit_service` resource support + +## v2.2.0 + +- [COOK-2303] - git::server support for RHEL `platform_family` + +## v2.1.4 + +- [COOK-2110] - initial test-kitchen support (only available in GitHub repository) +- [COOK-2253] - pin runit dependency + +## v2.1.2 + +- [COOK-2043] - install git on ubuntu 12.04 not git-core + +## v2.1.0 + +The repository didn't have pushed commits, and so the following changes from earlier-than-latest versions wouldn't be available on the community site. We're releasing 2.1.0 to correct this. + +- [COOK-1943] - Update to git 1.8.0 +- [COOK-2020] - Add setup option attributes to Git Windows package install + +## v2.0.0 + +This version uses `platform_family` attribute, making the cookbook incompatible with older versions of Chef/Ohai, hence the major version bump. + +- [COOK-1668] - git cookbook fails to run due to bad `platform_family` call +- [COOK-1759] - git::source needs additional package for rhel `platform_family` + +## v1.1.2 + +- [COOK-2020] - Add setup option attributes to Git Windows package install + +## v1.1.0 + +- [COOK-1943] - Update to git 1.8.0 + +## v1.0.2 + +- [COOK-1537] - add recipe for source installation + +## v1.0.0 + +- [COOK-1152] - Add support for Mac OS X +- [COOK-1112] - Add support for Windows + +## v0.10.0 + +- [COOK-853] - Git client installation on CentOS + +## v0.9.0 + +- Current public release diff --git a/cookbooks/git/CONTRIBUTING.md b/cookbooks/git/CONTRIBUTING.md new file mode 100644 index 0000000..ef2f2b8 --- /dev/null +++ b/cookbooks/git/CONTRIBUTING.md @@ -0,0 +1,2 @@ +Please refer to +https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/CONTRIBUTING.MD diff --git a/cookbooks/git/MAINTAINERS.md b/cookbooks/git/MAINTAINERS.md new file mode 100644 index 0000000..645ed14 --- /dev/null +++ b/cookbooks/git/MAINTAINERS.md @@ -0,0 +1,15 @@ + + +# Maintainers + +This file lists how this cookbook project is maintained. When making changes to the system, this file tells you who needs to review your patch - you need a review from an existing maintainer for the cookbook to provide a :+1: on your pull request. Additionally, you need to not receive a veto from a Lieutenant or the Project Lead. + +Check out [How Cookbooks are Maintained](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/CONTRIBUTING.MD) for details on the process and how to become a maintainer or the project lead. + +# Project Maintainer +* [Tim Smith](https://github.com/tas50) + +# Maintainers +* [Jennifer Davis](https://github.com/sigje) +* [Tim Smith](https://github.com/tas50) +* [Thom May](https://github.com/thommay) diff --git a/cookbooks/git/README.md b/cookbooks/git/README.md new file mode 100644 index 0000000..2196803 --- /dev/null +++ b/cookbooks/git/README.md @@ -0,0 +1,161 @@ +# Git Cookbook + +[![Build Status](https://travis-ci.org/chef-cookbooks/git.svg?branch=master)](https://travis-ci.org/chef-cookbooks/git) [![Cookbook Version](https://img.shields.io/cookbook/v/git.svg)](https://supermarket.chef.io/cookbooks/git) + +Installs git_client from package or source. Optionally sets up a git service under xinetd. + +## Scope + +This cookbook is concerned with the Git SCM utility. It does not address ecosystem tooling or related projects. + +## Requirements + +### Platforms + +The following platforms have been tested with Test Kitchen: + +``` +|---------------+-------| +| centos-6 | X | +|---------------+-------| +| centos-7 | X | +|---------------+-------| +| fedora | X | +|---------------+-------| +| debian-7 | X | +|---------------+-------| +| debian-8 | X | +|---------------+-------| +| ubuntu-14.04 | X | +|---------------+-------| +| ubuntu-16.04 | X | +|---------------+-------| +| openSUSE 13.2 | X | +|---------------+-------| +| openSUSE Leap | X | +|---------------+-------| +``` + +### Chef + +- Chef 12.5+ + +### Cookbooks + +- depends 'build-essential' - For compiling from source +- depends 'dmg' - For macOS Support +- depends 'yum-epel' - For older RHEL platform_family support + +## Usage + +Add `git::default`, `git::source` or `git::windows` to your run_list OR add `depends 'git', '~> 4.3'` to your cookbook's metadata.rb. include_recipe one of the recipes from your cookbook OR use the git_client resource directly, the same way you'd use core Chef resources (file, template, directory, package, etc). + +## Resources Overview + +- `git_client`: Manages a Git client installation on a machine. Acts as a singleton when using the (default) package provider. Source provider available as well. +- `git_service`: Sets up a Git service via xinetd. WARNING: This is insecure and will probably be removed in the future +- `git_config`: Sets up Git configuration on a node. + +### git_client + +The `git_client` resource manages the installation of a Git client on a machine. + +#### Example + +```ruby +git_client 'default' do + action :install +end +``` + +### git_config + +The `git_config` resource manages the configuration of Git client on a machine. + +#### Example + +```ruby +git_config 'url.https://github.com/.insteadOf' do + value 'git://github.com/' + scope 'system' + options '--add' +end +``` + +#### Properties + +Currently, there are distinct sets of resource properties, used by the providers for source, package, macos, and windows. + +# used by linux package providers + +- `package_name` - Package name to install on Linux machines. Defaults to a calculated value based on platform. +- `package_version` - Defaults to nil. +- `package_action` - Defaults to `:install` + +# used by source providers + +- `source_prefix` - Defaults to '/usr/local' +- `source_url` - Defaults to a calculated URL based on source_version +- `source_version` - Defaults to 2.7.4 +- `source_use_pcre` - configure option for build. Defaults to false +- `source_checksum` - Defaults to a known value for the 2.7.4 source tarball + +# used by OSX package providers + +- `osx_dmg_app_name` - Defaults to 'git-2.7.1-intel-universal-mavericks' +- `osx_dmg_package_id` - Defaults to 'GitOSX.Installer.git271.git.pkg' +- `osx_dmg_volumes_dir` - Defaults to 'Git 2.7.1 Mavericks Intel Universal' +- `osx_dmg_url` - Defaults to Sourceforge +- `osx_dmg_checksum` - Defaults to the value for 2.7.1 + +# used by the Windows package providers + +- `windows_display_name` - Windows display name +- `windows_package_url` - Defaults to the Internet +- `windows_package_checksum` - Defaults to the value for 2.7.4 + +## Recipes + +This cookbook ships with ready to use, attribute driven recipes that utilize the `git_client` and `git_service` resources. As of cookbook 4.x, they utilize the same attributes layout scheme from the 3.x. Due to some overlap, it is currently impossible to simultaneously install the Git client as a package and from source by using the "manipulate a the node attributes and run a recipe" technique. If you need both, you'll need to utilize the git_client resource in a recipe. + +## Attributes + +### Windows + +- `node['git']['version']` - git version to install +- `node['git']['url']` - URL to git package +- `node['git']['checksum']` - package SHA256 checksum +- `node['git']['display_name']` - `windows_package` resource Display Name (makes the package install idempotent) + +### Mac OS X + +- `node['git']['osx_dmg']['url']` - URL to git package +- `node['git']['osx_dmg']['checksum']` - package SHA256 checksum + +### Linux + +- `node['git']['prefix']` - git install directory +- `node['git']['version']` - git version to install +- `node['git']['url']` - URL to git tarball +- `node['git']['checksum']` - tarball SHA256 checksum +- `node['git']['use_pcre']` - if true, builds git with PCRE enabled + +## License & Authors + +- Author:: Joshua Timberman ([joshua@chef.io](mailto:joshua@chef.io)) +- Author:: Sean OMeara ([sean@sean.io](mailto:sean@sean.io)) +- Copyright:: 2009-2017, Chef Software, Inc. + +``` +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/cookbooks/git/attributes/default.rb b/cookbooks/git/attributes/default.rb new file mode 100644 index 0000000..a8b339e --- /dev/null +++ b/cookbooks/git/attributes/default.rb @@ -0,0 +1,47 @@ +# +# Author:: Jamie Winsor () +# Cookbook:: git +# Attributes:: default +# +# Copyright:: 2008-2016, Chef Software, Inc. +# +# 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. + +case node['platform_family'] +when 'windows' + default['git']['version'] = '2.8.1' + if node['kernel']['machine'] == 'x86_64' + default['git']['architecture'] = '64' + default['git']['checksum'] = '5e5283990cc91d1e9bd0858f8411e7d0afb70ce26e23680252fb4869288c7cfb' + else + default['git']['architecture'] = '32' + default['git']['checksum'] = '17418c2e507243b9c98db161e9e5e8041d958b93ce6078530569b8edaec6b8a4' + end + default['git']['url'] = 'https://github.com/git-for-windows/git/releases/download/v%{version}.windows.1/Git-%{version}-%{architecture}-bit.exe' + default['git']['display_name'] = "Git version #{node['git']['version']}" +when 'mac_os_x' + default['git']['osx_dmg']['app_name'] = 'git-2.8.1-intel-universal-mavericks' + default['git']['osx_dmg']['volumes_dir'] = 'Git 2.8.1 Mavericks Intel Universal' + default['git']['osx_dmg']['package_id'] = 'GitOSX.Installer.git281Universal.git.pkg' + default['git']['osx_dmg']['url'] = 'http://sourceforge.net/projects/git-osx-installer/files/git-2.8.1-intel-universal-mavericks.dmg/download' + default['git']['osx_dmg']['checksum'] = 'c2912895a1e2018d9be4c646765d511f7c82e0114275505dbd13d1ac70c62023' +else + default['git']['prefix'] = '/usr/local' + default['git']['version'] = '2.8.1' + default['git']['url'] = 'https://nodeload.github.com/git/git/tar.gz/v%{version}' + default['git']['checksum'] = 'e08503ecaf5d3ac10c40f22871c996a392256c8d038d16f52ebf974cba29ae42' + default['git']['use_pcre'] = false +end + +default['git']['server']['base_path'] = '/srv/git' +default['git']['server']['export_all'] = true diff --git a/cookbooks/git/libraries/helpers.rb b/cookbooks/git/libraries/helpers.rb new file mode 100644 index 0000000..bedde84 --- /dev/null +++ b/cookbooks/git/libraries/helpers.rb @@ -0,0 +1,48 @@ +module GitCookbook + module Helpers + # linux packages default to distro offering + def parsed_package_name + return new_resource.package_name if new_resource.package_name + return 'git-core' if node['platform'] == 'ubuntu' && node['platform_version'].to_f < 10.10 + return 'developer/versioning/git' if node['platform'] == 'omnios' + return 'scmgit' if node['platform'] == 'smartos' + 'git' + end + + def parsed_package_version + return new_resource.package_version if new_resource.package_version + end + + # source + def parsed_source_url + return new_resource.source_url if new_resource.source_url + "https://nodeload.github.com/git/git/tar.gz/v#{new_resource.source_version}" + end + + def parsed_source_checksum + return new_resource.source_checksum if new_resource.source_checksum + '8d53703d75890c03e26a915c7af3b7b98d8cfb94382f685a9bcbee1eeaec47b4' # 2.7.4 tarball + end + + # windows + def parsed_windows_display_name + return new_resource.windows_display_name if new_resource.windows_display_name + "Git version #{parsed_windows_package_version}" + end + + def parsed_windows_package_version + return new_resource.windows_package_version if new_resource.windows_package_version + '2.7.4' + end + + def parsed_windows_package_url + return new_resource.windows_package_url if new_resource.windows_package_url + "https://github.com/git-for-windows/git/releases/download/v%#{parsed_windows_package_version}.windows.1/Git-%#{parsed_windows_package_version}-32-bit.exe" + end + + def parsed_windows_package_checksum + return new_resource.windows_package_checksum if new_resource.windows_package_checksum + '49601d5102df249d6f866ecfa1eea68eb5672acc1dbb7e4051099e792f6da5fc' + end + end +end diff --git a/cookbooks/git/libraries/matchers.rb b/cookbooks/git/libraries/matchers.rb new file mode 100644 index 0000000..25ed8df --- /dev/null +++ b/cookbooks/git/libraries/matchers.rb @@ -0,0 +1,16 @@ +if defined?(ChefSpec) + ChefSpec.define_matcher(:git_client) + ChefSpec.define_matcher(:git_service) + + def set_git_config(resource_name) # rubocop:disable Style/AccessorMethodName + ChefSpec::Matchers::ResourceMatcher.new(:git_config, :set, resource_name) + end + + def install_git_client(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:git_client, :install, resource_name) + end + + def install_git_service(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:git_service, :install, resource_name) + end +end diff --git a/cookbooks/git/libraries/provider_git_client.rb b/cookbooks/git/libraries/provider_git_client.rb new file mode 100644 index 0000000..41d7cfd --- /dev/null +++ b/cookbooks/git/libraries/provider_git_client.rb @@ -0,0 +1,13 @@ +class Chef + class Provider + class GitClient < Chef::Provider::LWRPBase + use_inline_resources + + def whyrun_supported? + true + end + + include GitCookbook::Helpers + end + end +end diff --git a/cookbooks/git/libraries/provider_git_client_osx.rb b/cookbooks/git/libraries/provider_git_client_osx.rb new file mode 100644 index 0000000..db604c9 --- /dev/null +++ b/cookbooks/git/libraries/provider_git_client_osx.rb @@ -0,0 +1,26 @@ +class Chef + class Provider + class GitClient + class Osx < Chef::Provider::GitClient + include Chef::DSL::IncludeRecipe + + provides :git_client, os: 'mac_os_x' + + action :install do + dmg_package 'GitOSX-Installer' do + app new_resource.osx_dmg_app_name + package_id new_resource.osx_dmg_package_id + volumes_dir new_resource.osx_dmg_volumes_dir + source new_resource.osx_dmg_url + checksum new_resource.osx_dmg_checksum + type 'pkg' + action :install + end + end + + action :delete do + end + end + end + end +end diff --git a/cookbooks/git/libraries/provider_git_client_package.rb b/cookbooks/git/libraries/provider_git_client_package.rb new file mode 100644 index 0000000..8c750e3 --- /dev/null +++ b/cookbooks/git/libraries/provider_git_client_package.rb @@ -0,0 +1,27 @@ +class Chef + class Provider + class GitClient + class Package < Chef::Provider::GitClient + include Chef::DSL::IncludeRecipe + + provides :git_client, os: 'linux' + + action :install do + # FIXME: rhel 5 + include_recipe 'yum-epel' if node['platform_family'] == 'rhel' && node['platform_version'].to_i == 5 + + # Software installation + package "#{new_resource.name} :create #{parsed_package_name}" do + package_name parsed_package_name + version parsed_package_version + action new_resource.package_action + action :install + end + end + + action :delete do + end + end + end + end +end diff --git a/cookbooks/git/libraries/provider_git_client_source.rb b/cookbooks/git/libraries/provider_git_client_source.rb new file mode 100644 index 0000000..6ab8bc7 --- /dev/null +++ b/cookbooks/git/libraries/provider_git_client_source.rb @@ -0,0 +1,66 @@ +class Chef + class Provider + class GitClient + class Source < Chef::Provider::GitClient + include Chef::DSL::IncludeRecipe + + action :install do + return "#{node['platform']} is not supported by the #{cookbook_name}::#{recipe_name} recipe" unless platform_family?('rhel', 'suse', 'fedora', 'debian') + + include_recipe 'build-essential' + include_recipe 'yum-epel' if node['platform_family'] == 'rhel' && node['platform_version'].to_i == 5 + + # move this to attributes. + case node['platform_family'] + when 'fedora' + pkgs = %w(tar openssl-devel libcurl-devel expat-devel perl-ExtUtils-MakeMaker) + when 'rhel' + case node['platform_version'].to_i + when 5 + pkgs = %w(tar expat-devel gettext-devel curl-devel openssl-devel zlib-devel) + pkgs += %w( pcre-devel ) if new_resource.source_use_pcre + when 6, 7 + pkgs = %w(tar expat-devel gettext-devel libcurl-devel openssl-devel perl-ExtUtils-MakeMaker zlib-devel) + pkgs += %w( pcre-devel ) if new_resource.source_use_pcre + else + pkgs = %w(expat-devel gettext-devel curl-devel openssl-devel perl-ExtUtils-MakeMaker zlib-devel) if node['platform'] == 'amazon' + pkgs += %w( pcre-devel ) if new_resource.source_use_pcre + end + when 'debian' + pkgs = %w(libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev) + pkgs += %w( libpcre3-dev ) if new_resource.source_use_pcre + when 'suse' + pkgs = %w(tar libcurl-devel libexpat-devel gettext-tools zlib-devel libopenssl-devel) + pkgs += %w( libpcre2-devel ) if new_resource.source_use_pcre + end + + package pkgs + + # reduce line-noise-eyness + remote_file "#{Chef::Config['file_cache_path']}/git-#{new_resource.source_version}.tar.gz" do + source parsed_source_url # helpers.rb + checksum parsed_source_checksum # helpers.rb + mode '0644' + not_if "test -f #{Chef::Config['file_cache_path']}/git-#{new_resource.source_version}.tar.gz" + end + + # reduce line-noise-eyness + execute "Extracting and Building Git #{new_resource.source_version} from Source" do + cwd Chef::Config['file_cache_path'] + additional_make_params = '' + additional_make_params += 'USE_LIBPCRE=1' if new_resource.source_use_pcre + command <<-COMMAND + (mkdir git-#{new_resource.source_version} && tar -zxf git-#{new_resource.source_version}.tar.gz -C git-#{new_resource.source_version} --strip-components 1) + (cd git-#{new_resource.source_version} && make prefix=#{new_resource.source_prefix} #{additional_make_params} install) + COMMAND + not_if "git --version | grep #{new_resource.source_version}" + not_if "#{new_resource.source_prefix}/bin/git --version | grep #{new_resource.source_version}" + end + end + + action :delete do + end + end + end + end +end diff --git a/cookbooks/git/libraries/provider_git_client_windows.rb b/cookbooks/git/libraries/provider_git_client_windows.rb new file mode 100644 index 0000000..305cdd2 --- /dev/null +++ b/cookbooks/git/libraries/provider_git_client_windows.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true +class Chef + class Provider + class GitClient + class Windows < Chef::Provider::GitClient + include Chef::DSL::IncludeRecipe + + provides :git_client, os: 'windows' + + action :install do + windows_package parsed_windows_display_name do + action :install + source parsed_windows_package_url + checksum parsed_windows_package_checksum + installer_type :inno + end + + # Git is installed to Program Files (x86) on 64-bit machines and + # 'Program Files' on 32-bit machines + PROGRAM_FILES = if node['git']['architecture'] == '32' + ENV['ProgramFiles(x86)'] || ENV['ProgramFiles'] + else + ENV['ProgramW6432'] || ENV['ProgramFiles'] + end + GIT_PATH = "#{PROGRAM_FILES}\\Git\\Cmd".freeze + + # COOK-3482 - windows_path resource doesn't change the current process + # environment variables. Therefore, git won't actually be on the PATH + # until the next chef-client run + ruby_block 'Add Git Path' do + block do + ENV['PATH'] += ";#{GIT_PATH}" + end + not_if { ENV['PATH'] =~ /GIT_PATH/ } + action :nothing + end + + windows_path GIT_PATH do + notifies :create, 'ruby_block[Add Git Path]', :immediately + action :add + end + end + + action :delete do + end + end + end + end +end diff --git a/cookbooks/git/libraries/provider_git_service.rb b/cookbooks/git/libraries/provider_git_service.rb new file mode 100644 index 0000000..7f74e90 --- /dev/null +++ b/cookbooks/git/libraries/provider_git_service.rb @@ -0,0 +1,57 @@ +class Chef + class Provider + class GitClient < Chef::Provider::LWRPBase + use_inline_resources + + def whyrun_supported? + true + end + + include Chef::DSL::IncludeRecipe + include GitCookbook::Helpers + + provides :git_service, os: 'linux' + + action :create do + return "#{node['platform']} is not supported by the #{cookbook_name}::#{recipe_name} recipe" if node['platform'] == 'windows' + + include_recipe 'git' + + directory new_resource.service_base_path do + owner 'root' + group 'root' + mode '0755' + end + + case node['platform_family'] + when 'debian' + package 'xinetd' + when 'rhel' + package 'git-daemon' + else + log 'Platform requires setting up a git daemon service script.' + log "Hint: /usr/bin/git daemon --export-all --user=nobody --group=daemon --base-path=#{new_resource.service_base_path}" + return + end + + template '/etc/xinetd.d/git' do + backup false + source 'git-xinetd.d.erb' + owner 'root' + group 'root' + mode '0644' + variables( + git_daemon_binary: value_for_platform_family( + 'debian' => '/usr/lib/git-core/git-daemon', + 'rhel' => '/usr/libexec/git-core/git-daemon' + ) + ) + end + + service 'xinetd' do + action [:enable, :restart] + end + end + end + end +end diff --git a/cookbooks/git/libraries/provider_git_service_xinetd.rb b/cookbooks/git/libraries/provider_git_service_xinetd.rb new file mode 100644 index 0000000..74b524e --- /dev/null +++ b/cookbooks/git/libraries/provider_git_service_xinetd.rb @@ -0,0 +1,55 @@ +class Chef + class Provider + class GitClient < Chef::Provider::LWRPBase + use_inline_resources + + def whyrun_supported? + true + end + + include Chef::DSL::IncludeRecipe + include GitCookbook::Helpers + + action :create do + return "#{node['platform']} is not supported by the #{cookbook_name}::#{recipe_name} recipe" if node['platform'] == 'windows' + + include_recipe 'git' + + directory new_resource.service_base_path do + owner 'root' + group 'root' + mode '0755' + end + + case node['platform_family'] + when 'debian' + package 'xinetd' + when 'rhel' + package 'git-daemon' + else + log 'Platform requires setting up a git daemon service script.' + log "Hint: /usr/bin/git daemon --export-all --user=nobody --group=daemon --base-path=#{new_resource.service_base_path}" + return + end + + template '/etc/xinetd.d/git' do + backup false + source 'git-xinetd.d.erb' + owner 'root' + group 'root' + mode '0644' + variables( + git_daemon_binary: value_for_platform_family( + 'debian' => '/usr/lib/git-core/git-daemon', + 'rhel' => '/usr/libexec/git-core/git-daemon' + ) + ) + end + + service 'xinetd' do + action [:enable, :restart] + end + end + end + end +end diff --git a/cookbooks/git/libraries/resource_git_client.rb b/cookbooks/git/libraries/resource_git_client.rb new file mode 100644 index 0000000..c426ebe --- /dev/null +++ b/cookbooks/git/libraries/resource_git_client.rb @@ -0,0 +1,38 @@ +require 'chef/resource/lwrp_base' + +class Chef + class Resource + class GitClient < Chef::Resource::LWRPBase + self.resource_name = :git_client + actions :install, :remove + default_action :install + + provides :git_client + + # used by source providers + attribute :source_checksum, kind_of: String, default: nil + attribute :source_prefix, kind_of: String, default: '/usr/local' + attribute :source_url, kind_of: String, default: nil + attribute :source_use_pcre, kind_of: [TrueClass, FalseClass], default: false + attribute :source_version, kind_of: String, default: nil + + # used by linux package providers + attribute :package_name, kind_of: String, default: nil + attribute :package_version, kind_of: String, default: nil + attribute :package_action, kind_of: Symbol, default: :install + + # used by OSX package providers + attribute :osx_dmg_app_name, kind_of: String, default: 'git-2.7.1-intel-universal-mavericks' + attribute :osx_dmg_package_id, kind_of: String, default: 'GitOSX.Installer.git271.git.pkg' + attribute :osx_dmg_volumes_dir, kind_of: String, default: 'Git 2.7.1 Mavericks Intel Universal' + attribute :osx_dmg_url, kind_of: String, default: 'http://sourceforge.net/projects/git-osx-installer/files/git-2.7.1-intel-universal-mavericks.dmg/download' + attribute :osx_dmg_checksum, kind_of: String, default: '260b32e8877eb72d07807b26163aeec42e2d98c350f32051ab1ff0cc33626440' # 2.7.1 + + # used by Windows providers + attribute :windows_display_name, kind_of: String, default: nil + attribute :windows_package_url, kind_of: String, default: nil + attribute :windows_package_checksum, kind_of: String, default: nil + attribute :windows_package_version, kind_of: String, default: nil + end + end +end diff --git a/cookbooks/git/libraries/resource_git_service.rb b/cookbooks/git/libraries/resource_git_service.rb new file mode 100644 index 0000000..a1f2343 --- /dev/null +++ b/cookbooks/git/libraries/resource_git_service.rb @@ -0,0 +1,16 @@ +require 'chef/resource/lwrp_base' + +class Chef + class Resource + class GitService < Chef::Resource::LWRPBase + self.resource_name = :git_service + actions :create + default_action :create + + provides :git_service + + # used by the service xinetd provider + attribute :service_base_path, kind_of: String, default: '/srv/git' + end + end +end diff --git a/cookbooks/git/metadata.json b/cookbooks/git/metadata.json new file mode 100644 index 0000000..28f80e7 --- /dev/null +++ b/cookbooks/git/metadata.json @@ -0,0 +1 @@ +{"name":"git","version":"6.0.0","description":"Installs git and/or sets up a Git server daemon","long_description":"# Git Cookbook\n\n[![Build Status](https://travis-ci.org/chef-cookbooks/git.svg?branch=master)](https://travis-ci.org/chef-cookbooks/git) [![Cookbook Version](https://img.shields.io/cookbook/v/git.svg)](https://supermarket.chef.io/cookbooks/git)\n\nInstalls git_client from package or source. Optionally sets up a git service under xinetd.\n\n## Scope\n\nThis cookbook is concerned with the Git SCM utility. It does not address ecosystem tooling or related projects.\n\n## Requirements\n\n### Platforms\n\nThe following platforms have been tested with Test Kitchen:\n\n```\n|---------------+-------|\n| centos-6 | X |\n|---------------+-------|\n| centos-7 | X |\n|---------------+-------|\n| fedora | X |\n|---------------+-------|\n| debian-7 | X |\n|---------------+-------|\n| debian-8 | X |\n|---------------+-------|\n| ubuntu-14.04 | X |\n|---------------+-------|\n| ubuntu-16.04 | X |\n|---------------+-------|\n| openSUSE 13.2 | X |\n|---------------+-------|\n| openSUSE Leap | X |\n|---------------+-------|\n```\n\n### Chef\n\n- Chef 12.5+\n\n### Cookbooks\n\n- depends 'build-essential' - For compiling from source\n- depends 'dmg' - For macOS Support\n- depends 'yum-epel' - For older RHEL platform_family support\n\n## Usage\n\nAdd `git::default`, `git::source` or `git::windows` to your run_list OR add `depends 'git', '~> 4.3'` to your cookbook's metadata.rb. include_recipe one of the recipes from your cookbook OR use the git_client resource directly, the same way you'd use core Chef resources (file, template, directory, package, etc).\n\n## Resources Overview\n\n- `git_client`: Manages a Git client installation on a machine. Acts as a singleton when using the (default) package provider. Source provider available as well.\n- `git_service`: Sets up a Git service via xinetd. WARNING: This is insecure and will probably be removed in the future\n- `git_config`: Sets up Git configuration on a node.\n\n### git_client\n\nThe `git_client` resource manages the installation of a Git client on a machine.\n\n#### Example\n\n```ruby\ngit_client 'default' do\n action :install\nend\n```\n\n### git_config\n\nThe `git_config` resource manages the configuration of Git client on a machine.\n\n#### Example\n\n```ruby\ngit_config 'url.https://github.com/.insteadOf' do\n value 'git://github.com/'\n scope 'system'\n options '--add'\nend\n```\n\n#### Properties\n\nCurrently, there are distinct sets of resource properties, used by the providers for source, package, macos, and windows.\n\n# used by linux package providers\n\n- `package_name` - Package name to install on Linux machines. Defaults to a calculated value based on platform.\n- `package_version` - Defaults to nil.\n- `package_action` - Defaults to `:install`\n\n# used by source providers\n\n- `source_prefix` - Defaults to '/usr/local'\n- `source_url` - Defaults to a calculated URL based on source_version\n- `source_version` - Defaults to 2.7.4\n- `source_use_pcre` - configure option for build. Defaults to false\n- `source_checksum` - Defaults to a known value for the 2.7.4 source tarball\n\n# used by OSX package providers\n\n- `osx_dmg_app_name` - Defaults to 'git-2.7.1-intel-universal-mavericks'\n- `osx_dmg_package_id` - Defaults to 'GitOSX.Installer.git271.git.pkg'\n- `osx_dmg_volumes_dir` - Defaults to 'Git 2.7.1 Mavericks Intel Universal'\n- `osx_dmg_url` - Defaults to Sourceforge\n- `osx_dmg_checksum` - Defaults to the value for 2.7.1\n\n# used by the Windows package providers\n\n- `windows_display_name` - Windows display name\n- `windows_package_url` - Defaults to the Internet\n- `windows_package_checksum` - Defaults to the value for 2.7.4\n\n## Recipes\n\nThis cookbook ships with ready to use, attribute driven recipes that utilize the `git_client` and `git_service` resources. As of cookbook 4.x, they utilize the same attributes layout scheme from the 3.x. Due to some overlap, it is currently impossible to simultaneously install the Git client as a package and from source by using the \"manipulate a the node attributes and run a recipe\" technique. If you need both, you'll need to utilize the git_client resource in a recipe.\n\n## Attributes\n\n### Windows\n\n- `node['git']['version']` - git version to install\n- `node['git']['url']` - URL to git package\n- `node['git']['checksum']` - package SHA256 checksum\n- `node['git']['display_name']` - `windows_package` resource Display Name (makes the package install idempotent)\n\n### Mac OS X\n\n- `node['git']['osx_dmg']['url']` - URL to git package\n- `node['git']['osx_dmg']['checksum']` - package SHA256 checksum\n\n### Linux\n\n- `node['git']['prefix']` - git install directory\n- `node['git']['version']` - git version to install\n- `node['git']['url']` - URL to git tarball\n- `node['git']['checksum']` - tarball SHA256 checksum\n- `node['git']['use_pcre']` - if true, builds git with PCRE enabled\n\n## License & Authors\n\n- Author:: Joshua Timberman ([joshua@chef.io](mailto:joshua@chef.io))\n- Author:: Sean OMeara ([sean@sean.io](mailto:sean@sean.io))\n- Copyright:: 2009-2017, Chef Software, Inc.\n\n```\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","maintainer":"Chef Software, Inc.","maintainer_email":"cookbooks@chef.io","license":"Apache 2.0","platforms":{"amazon":">= 0.0.0","centos":">= 0.0.0","debian":">= 0.0.0","fedora":">= 0.0.0","freebsd":">= 0.0.0","mac_os_x":">= 10.6.0","omnios":">= 0.0.0","oracle":">= 0.0.0","redhat":">= 0.0.0","smartos":">= 0.0.0","scientific":">= 0.0.0","suse":">= 0.0.0","opensuse":">= 0.0.0","opensuseleap":">= 0.0.0","ubuntu":">= 0.0.0","windows":">= 0.0.0"},"dependencies":{"build-essential":">= 0.0.0","dmg":">= 0.0.0","yum-epel":">= 0.0.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{"git":"Installs git","git::server":"Sets up a runit_service for git daemon","git::source":"Installs git from source"}} \ No newline at end of file diff --git a/cookbooks/postgresql/files/default/tests/minitest/support/helpers.rb b/cookbooks/git/recipes/default.rb similarity index 65% rename from cookbooks/postgresql/files/default/tests/minitest/support/helpers.rb rename to cookbooks/git/recipes/default.rb index fd8fcea..bc7feed 100644 --- a/cookbooks/postgresql/files/default/tests/minitest/support/helpers.rb +++ b/cookbooks/git/recipes/default.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: postgresql_test +# Cookbook:: git # Recipe:: default # -# Copyright 2012, Opscode, Inc. +# Copyright:: 2008-2016, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,15 +15,5 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# -module Helpers - module Postgresql - require 'chef/mixin/shell_out' - include Chef::Mixin::ShellOut - include MiniTest::Chef::Assertions - include MiniTest::Chef::Context - include MiniTest::Chef::Resources - - end -end +include_recipe 'git::package' diff --git a/cookbooks/git/recipes/package.rb b/cookbooks/git/recipes/package.rb new file mode 100644 index 0000000..e8812f6 --- /dev/null +++ b/cookbooks/git/recipes/package.rb @@ -0,0 +1,37 @@ +# +# Cookbook:: git +# Recipe:: package +# +# Copyright:: 2008-2016, Chef Software, Inc. +# +# 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. + +case node['platform'] +when 'mac_os_x' + # FIXME: The resource has three distinct groups of properties used in + # different providers... should we make multiple resource types instead? + git_client 'default' do + osx_dmg_app_name node['git']['osx_dmg']['app_name'] + osx_dmg_package_id node['git']['osx_dmg']['package_id'] + osx_dmg_volumes_dir node['git']['osx_dmg']['volumes_dir'] + osx_dmg_url node['git']['osx_dmg']['url'] + osx_dmg_checksum node['git']['osx_dmg']['checksum'] + action :install + end +when 'windows' + include_recipe 'git::windows' +else + git_client 'default' do + action :install + end +end diff --git a/cookbooks/postgresql/files/default/tests/minitest/ruby_test.rb b/cookbooks/git/recipes/server.rb similarity index 63% rename from cookbooks/postgresql/files/default/tests/minitest/ruby_test.rb rename to cookbooks/git/recipes/server.rb index 3b3649f..1567cbb 100644 --- a/cookbooks/postgresql/files/default/tests/minitest/ruby_test.rb +++ b/cookbooks/git/recipes/server.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: postgresql_test -# Recipe:: default +# Cookbook:: git +# Recipe:: server # -# Copyright 2012, Opscode, Inc. +# Copyright:: 2009-2016, Chef Software, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,14 +15,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# -require File.expand_path('../support/helpers', __FILE__) - -describe 'postgresql::ruby' do - include Helpers::Postgresql - - it 'installs the pg gem in Chefs ruby environment' do - assert Gem::Specification.all_names.grep("pg-.*") - end +git_service 'default' do + service_base_path node['git']['server']['base_path'] + action :create end diff --git a/cookbooks/application_nodejs/resources/nodejs.rb b/cookbooks/git/recipes/source.rb similarity index 54% rename from cookbooks/application_nodejs/resources/nodejs.rb rename to cookbooks/git/recipes/source.rb index 1ee52b3..73429d5 100644 --- a/cookbooks/application_nodejs/resources/nodejs.rb +++ b/cookbooks/git/recipes/source.rb @@ -1,9 +1,8 @@ # -# Author:: Conrad Kramer -# Cookbook Name:: application_node -# Resource:: node +# Cookbook:: git +# Recipe:: source # -# Copyright:: 2013, Kramer Software Productions, LLC. +# Copyright:: 2012-2016, Brian Flad, Fletcher Nichol # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,10 +15,14 @@ # 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. -# -include ApplicationCookbook::ResourceBase - -attribute :npm, :kind_of => [NilClass, TrueClass, FalseClass], :default => true -attribute :template, :kind_of => [String, NilClass], :default => nil -attribute :entry_point, :kind_of => String, :default => 'app.js' +# drive version from node attributes +git_client 'default' do + provider Chef::Provider::GitClient::Source + source_checksum node['git']['checksum'] + source_prefix node['git']['prefix'] + source_url format(node['git']['url'], version: node['git']['version']) + source_use_pcre node['git']['use_pcre'] + source_version node['git']['version'] + action :install +end diff --git a/cookbooks/git/recipes/windows.rb b/cookbooks/git/recipes/windows.rb new file mode 100644 index 0000000..7fd5882 --- /dev/null +++ b/cookbooks/git/recipes/windows.rb @@ -0,0 +1,24 @@ +# +# Cookbook:: git +# Recipe:: windows +# +# Copyright:: 2008-2016, Chef Software, Inc. +# +# 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. + +git_client 'default' do + windows_display_name node['git']['display_name'] + windows_package_url format(node['git']['url'], version: node['git']['version'], architecture: node['git']['architecture']) + windows_package_checksum node['git']['checksum'] + action :install +end diff --git a/cookbooks/git/resources/config.rb b/cookbooks/git/resources/config.rb new file mode 100644 index 0000000..2f3d88c --- /dev/null +++ b/cookbooks/git/resources/config.rb @@ -0,0 +1,49 @@ +property :key, String, name_attribute: true +property :value, String +property :scope, equal_to: %w(local global system), default: 'global', desired_state: false +property :path, String, desired_state: false +property :user, String, desired_state: false +property :group, String, desired_state: false +property :options, String, desired_state: false + +attr_accessor :exists + +require 'mixlib/shellout' + +def initialize(*args) + super + + @run_context.include_recipe 'git' +end + +load_current_value do + cmd_env = user ? { 'USER' => user, 'HOME' => ::Dir.home(user) } : nil + config_vals = Mixlib::ShellOut.new("git config --get --#{scope} #{key}", user: user, group: group, cwd: path, env: cmd_env) + config_vals.run_command + if config_vals.stdout.empty? + value nil + else + value config_vals.stdout.chomp + end +end + +action :set do + converge_if_changed do + execute "#{config_cmd} #{new_resource.key} \"#{new_resource.value}\" #{new_resource.options}".rstrip do + cwd new_resource.path + user new_resource.user + group new_resource.group + environment cmd_env + end + end +end + +action_class.class_eval do + def config_cmd + "git config --#{new_resource.scope}" + end + + def cmd_env + new_resource.user ? { 'USER' => new_resource.user, 'HOME' => ::Dir.home(new_resource.user) } : nil + end +end diff --git a/cookbooks/git/templates/default/git-xinetd.d.erb b/cookbooks/git/templates/default/git-xinetd.d.erb new file mode 100644 index 0000000..3c6bb3b --- /dev/null +++ b/cookbooks/git/templates/default/git-xinetd.d.erb @@ -0,0 +1,10 @@ +service git +{ + disable = no + socket_type = stream + wait = no + user = nobody + server = <%= @git_daemon_binary %> + server_args = --base-path=<%= node["git"]["server"]["base_path"] %> <%= node['git']['server']['export_all'] ? '--export-all' : nil %> --syslog --inetd --verbose + log_on_failure += USERID +} diff --git a/cookbooks/git/templates/default/sv-git-daemon-log-run.erb b/cookbooks/git/templates/default/sv-git-daemon-log-run.erb new file mode 100644 index 0000000..a79a518 --- /dev/null +++ b/cookbooks/git/templates/default/sv-git-daemon-log-run.erb @@ -0,0 +1,2 @@ +#!/bin/sh +exec svlogd -tt ./main diff --git a/cookbooks/git/templates/default/sv-git-daemon-run.erb b/cookbooks/git/templates/default/sv-git-daemon-run.erb new file mode 100644 index 0000000..f340a23 --- /dev/null +++ b/cookbooks/git/templates/default/sv-git-daemon-run.erb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec /usr/bin/git daemon <%= node['git']['server']['export_all'] ? '--export-all' : nil %> --user=nobody --group=daemon --syslog --base-path=<%= node["git"]["server"]["base_path"] %> <%= node["git"]["server"]["base_path"] %> diff --git a/cookbooks/mediawiki/recipes/database.rb b/cookbooks/mediawiki/recipes/database.rb index df4a145..382d65c 100644 --- a/cookbooks/mediawiki/recipes/database.rb +++ b/cookbooks/mediawiki/recipes/database.rb @@ -22,6 +22,11 @@ end socket = "/var/run/mysql-#{db['instance_name']}/mysqld.sock" if node['platform_family'] == 'debian' + directory "/var/run/mysqld" do + action :create + owner "mysql" + group "mysql" + end link '/var/run/mysqld/mysqld.sock' do to socket not_if 'test -f /var/run/mysqld/mysqld.sock' diff --git a/cookbooks/mediawiki/recipes/default.rb b/cookbooks/mediawiki/recipes/default.rb index 17e55d4..34e251c 100644 --- a/cookbooks/mediawiki/recipes/default.rb +++ b/cookbooks/mediawiki/recipes/default.rb @@ -10,7 +10,15 @@ include_recipe "apt" include_recipe "php::default" -include_recipe "php::module_apc" + +if node['platform'] == 'ubuntu' and node['platform_version'] >= '16.04' + # APC is now apcu in PHP 7 + include_recipe "php::module_apcu" + # Dependency + package "php7.0-mbstring" +else + include_recipe "php::module_apc" +end include_recipe "php::module_mysql" include_recipe "mediawiki::database" @@ -38,8 +46,11 @@ when "debian" package "libicu-dev" end -php_pear "intl" do - action :install +if platform?('ubuntu') && node[:platform_version].to_f < 16.04 + # bundled with PHP since version 5.3 + php_pear "intl" do + action :install + end end # Configure mediawiki database diff --git a/cookbooks/mediawiki/recipes/nginx.rb b/cookbooks/mediawiki/recipes/nginx.rb index 79561cb..22ff455 100644 --- a/cookbooks/mediawiki/recipes/nginx.rb +++ b/cookbooks/mediawiki/recipes/nginx.rb @@ -7,32 +7,7 @@ node.set_unless['php-fpm']['pools'] = [] include_recipe "php-fpm" include_recipe 'php-fpm::repository' unless node['php-fpm']['skip_repository_install'] - -if node['php-fpm']['package_name'].nil? - if platform_family?("rhel") - php_fpm_package_name = "php-fpm" - else - php_fpm_package_name = "php5-fpm" - end -else - php_fpm_package_name = node['php-fpm']['package_name'] -end - -package php_fpm_package_name do - action :install -end - -if node['php-fpm']['service_name'].nil? - php_fpm_service_name = php_fpm_package_name -else - php_fpm_service_name = node['php-fpm']['service_name'] -end - -service "php-fpm" do - service_name php_fpm_service_name - supports :start => true, :stop => true, :restart => true, :reload => true - action [ :enable, :start ] -end +include_recipe "php-fpm::install" php_fpm_pool "www" do enable false diff --git a/cookbooks/poise-archive/CHANGELOG.md b/cookbooks/poise-archive/CHANGELOG.md new file mode 100644 index 0000000..f95577a --- /dev/null +++ b/cookbooks/poise-archive/CHANGELOG.md @@ -0,0 +1,38 @@ +# Poise-Archive Changelog + +## v1.4.0 + +* Added support for using 7-Zip on Windows. +* Fixed handling of `.tar.xz` archives on RHEL and CentOS. + +## v1.3.0 + +* Add support for unpacking directly from a URL. + +## v1.2.1 + +* [#1](https://github.com/poise/poise-archive/issues/1) Restore file permissions + for ZIP files. + +## v1.2.0 + +* Add back a tar-binary provider called `GnuTar`, used by default on Linux. +* Support for ZIP files via RubyZip. +* Full Windows support, including with the `user` and `group` properties. + +## v1.1.2 + +* Fix compat with older Ruby that doesn't include `Entry#symlink?`. + +## v1.1.1 + +* Fix GNU tar longlink extension. + +## v1.1.0 + +* Scrap the original tar implementation in favor of a 100% pure-Ruby solution. + This should work on all platforms exactly the same. Hopefully. + +## v1.0.0 + +* Initial release! diff --git a/cookbooks/poise-archive/README.md b/cookbooks/poise-archive/README.md new file mode 100644 index 0000000..46893e6 --- /dev/null +++ b/cookbooks/poise-archive/README.md @@ -0,0 +1,103 @@ +# Poise-Archive Cookbook + +[![Build Status](https://img.shields.io/travis/poise/poise-archive.svg)](https://travis-ci.org/poise/poise-archive) +[![Gem Version](https://img.shields.io/gem/v/poise-archive.svg)](https://rubygems.org/gems/poise-archive) +[![Cookbook Version](https://img.shields.io/cookbook/v/poise-archive.svg)](https://supermarket.chef.io/cookbooks/poise-archive) +[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-archive.svg)](https://codecov.io/github/poise/poise-archive) +[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-archive.svg)](https://gemnasium.com/poise/poise-archive) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +A [Chef](https://www.chef.io/) cookbook to unpack file archives. + +It supports `.tar`, `.tar.gz`, `.tar.bz2`, and `.zip` archive files. + +## Quick Start + +To download an unpack and archive: + +```ruby +poise_archive 'https://example.com/myapp.tgz' do + destination '/opt/myapp' +end +``` + +## Requirements + +Chef 12.1 or newer is required. + +## Platforms + +This cookbook supports all platforms (including Windows) but some Unix platforms +(Solaris, AIX) may see very slow tar file unpacking when using the pure-Ruby fallback +implementation. + +## Resources + +### `poise_archive` + +The `poise_archive` resource unpacks file archives. + +```ruby +poise_archive '/tmp/myapp-1.2.0.tar' do + destination '/srv/myapp-1.2.0' +end +``` + +A URL can also be passed as the source path, optionally with extra properties to +be merged with `source_properties`. + +```ruby +poise_archive 'http://example.com/myapp-1.2.0.zip' do + destination '/srv/myapp-1.2.0' +end + +poise_archive ['http://example.com/myapp-1.2.0.zip', {headers: {'Authentication' => '...'}}] do + destination '/srv/myapp-1.2.0' +end +``` + +#### Actions + +* `:unpack` – Unpack the archive. *(default)* + +#### Properties + +* `path` – Path to the archive. If relative, it is taken as a file inside + `Chef::Config[:file_cache_path]`. If a URL, it is downloaded to a cache file + first. *(name attribute)* +* `destination` – Path to unpack the archive to. If not specified, the path of + the archive without the file extension is used. Required when unpacking from + a URL. *(default: auto)* +* `group` – Group to run the unpack as. +* `keep_existing` – Keep existing files in the destination directory when + unpacking. *(default: false)* +* `source_properties` – Property key/value pairs to be applied to the + `remote_file` file resource when downloading a URL. *(default: {retries: 5})* +* `strip_components` – Number of intermediary directories to skip when + unpacking. Works like GNU tar's `--strip-components`. *(default: 1)* +* `user` – User to run the unpack as. + +## Sponsors + +Development sponsored by [Bloomberg](http://www.bloomberg.com/company/technology/). + +The Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/). + +## License + +Copyright 2016-2017, Noah Kantrowitz + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +BZip2 implementation is based on RBzip2. Copyright Sebastian Staudt, Brian Lopez. +RBzip2 code used under the terms of the new BSD license. diff --git a/cookbooks/poise-archive/attributes/default.rb b/cookbooks/poise-archive/attributes/default.rb new file mode 100644 index 0000000..5c92d99 --- /dev/null +++ b/cookbooks/poise-archive/attributes/default.rb @@ -0,0 +1,18 @@ +# +# Copyright 2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['poise-archive']['seven_zip']['version'] = '16.04' +default['poise-archive']['seven_zip']['url'] = 'http://www.7-zip.org/a/7z%{version_tag}%{arch_tag}.exe' diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive.rb new file mode 100644 index 0000000..e796045 --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive.rb @@ -0,0 +1,21 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseArchive + autoload :Resources, 'poise_archive/resources' + autoload :VERSION, 'poise_archive/version' +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers.rb new file mode 100644 index 0000000..8d0d29f --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers.rb @@ -0,0 +1,38 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/platform/provider_priority_map' + +require 'poise_archive/archive_providers/gnu_tar' +require 'poise_archive/archive_providers/seven_zip' +require 'poise_archive/archive_providers/tar' +require 'poise_archive/archive_providers/zip' + + +module PoiseArchive + # Providers for the poise_archive resource. + # + # @since 1.0.0 + module ArchiveProviders + # Set up priority maps + Chef::Platform::ProviderPriorityMap.instance.priority(:poise_archive, [ + PoiseArchive::ArchiveProviders::Zip, + PoiseArchive::ArchiveProviders::GnuTar, + PoiseArchive::ArchiveProviders::SevenZip, + PoiseArchive::ArchiveProviders::Tar, + ]) + end +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/base.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/base.rb new file mode 100644 index 0000000..a249e72 --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/base.rb @@ -0,0 +1,132 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'fileutils' + +require 'chef/provider' +require 'poise' + + +module PoiseArchive + module ArchiveProviders + # The provider base class for `poise_archive`. + # + # @see PoiseArchive::Resources::PoiseArchive::Resource + # @provides poise_archive + class Base < Chef::Provider + include Poise + + # Set the file extension this provider will handle. + # + # @param match [RegExp] Regular expression to match against the archive + # file name. + # @return [void] + # @example + # class MyProvider < Base + # provides_extension(/\.hqx$/) + # end + def self.provides_extension(match) + provides(:poise_archive) + @provides_extension = match + end + + # Override normal provider resolution to also check file extension if one + # was specified in the provider. + # + # @api private + def self.provides?(node, resource) + super && (!@provides_extension || @provides_extension.match(resource.path)) + end + + # `unpack` action for `poise_archive`. + # + # @return [void] + def action_unpack + if new_resource.is_url? + download_resource = download_file + # Check if the download resource updated, if not don't run the rest + # of the unpack for idempotence. I could also check + # new_resource.updated? but this seems more future proof. + return if !download_resource.updated_by_last_action? + end + converge_by("unpack archive #{new_resource.path} to #{new_resource.destination}") do + notifying_block do + create_directory + end + empty_directory + unpack_archive + end + end + + private + + # Download the source file to a cache path. + # + # @return [Chef::Resource] + def download_file + # resource_state used for closure breaking on the notifying block. + resource_state = [] + notifying_block do + # TODO handle cookbook:// for cookbook_file "downloads". + resource_state << remote_file(new_resource.absolute_path) do + source new_resource.path + retries 5 # As a default, could be overridden by source_properties. + new_resource.merged_source_properties.each do |key, value| + send(key, value) + end + end + end + # Return the download resource for state tracking. + resource_state.first + end + + # Make sure the destination directory exists. + # + # @return [void] + def create_directory + directory new_resource.destination do + group new_resource.group if new_resource.group + owner new_resource.user if new_resource.user + # There is explicitly no mode being set here. If a non-default mode + # is needed, you should manage that outside of poise_archive. + end + end + + # Remove all existing content from the destination so we can unpack the + # new content. + # + # @return [void] + def empty_directory + # If you want to keep it, not my problem. + return if new_resource.keep_existing + dest = new_resource.destination + Dir.entries(dest).each do |entry| + next if entry == '.' || entry == '..' + FileUtils.remove_entry_secure(::File.join(dest, entry)) + end + end + + # Run the provider-specific unpack behavior. + # + # @abstract + # @return [void] + def unpack_archive + raise NotImplementedError + end + + end + end +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/gnu_tar.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/gnu_tar.rb new file mode 100644 index 0000000..196f844 --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/gnu_tar.rb @@ -0,0 +1,88 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'fileutils' +require 'tmpdir' + +require 'poise_archive/archive_providers/base' + + +module PoiseArchive + module ArchiveProviders + # The `gnu_tar` provider class for `poise_archive` to install from TAR + # archives using GNU's tar executable. + # + # @see PoiseArchive::Resources::PoiseArchive::Resource + # @provides poise_archive + class GnuTar < Base + provides_extension(/\.t(ar|gz|bz|xz)/) + + # Only use this if we are on Linux. Everyone else gets the slow Ruby code. + # + # @api private + def self.provides?(node, _resource) + super && node['os'] == 'linux' + end + + private + + def unpack_archive + notifying_block do + install_prereqs + end + unpack_tar + end + + # Install any needed prereqs. + # + # @return [void] + def install_prereqs + utils = ['tar'] + utils << 'bzip2' if new_resource.absolute_path =~ /\.t?bz/ + if new_resource.absolute_path =~ /\.t?xz/ + xz_package = node.value_for_platform_family( + debian: 'xz-utils', + rhel: 'xz', + ) + utils << xz_package if xz_package + end + # This is a resource. + package utils + end + + # Unpack the archive and process `strip_components`. + # + # @return [void] + def unpack_tar + # Build the tar command. + cmd = %w{tar} + cmd << "--strip-components=#{new_resource.strip_components}" if new_resource.strip_components && new_resource.strip_components > 0 + cmd << if new_resource.absolute_path =~ /\.t?gz/ + '-xzvf' + elsif new_resource.absolute_path =~ /\.t?bz/ + '-xjvf' + elsif new_resource.absolute_path =~ /\.t?xz/ + '-xJvf' + else + '-xvf' + end + cmd << new_resource.absolute_path + poise_shell_out!(cmd, cwd: new_resource.destination, group: new_resource.group, user: new_resource.user) + end + + end + end +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/seven_zip.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/seven_zip.rb new file mode 100644 index 0000000..59ed852 --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/seven_zip.rb @@ -0,0 +1,188 @@ +# +# Copyright 2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'fileutils' +require 'tmpdir' + + +require 'poise_archive/archive_providers/base' + + +module PoiseArchive + module ArchiveProviders + # The `seven_zip` provider class for `poise_archive` to upack archives + # using 7-Zip. + # + # @since 1.4.0 + # @see PoiseArchive::Resources::PoiseArchive::Resource + # @provides poise_archive + class SevenZip < Base + provides_extension(/\.(t(ar|gz|bz|xz)|zip|7z)/) + + # Only works on Windows, because use less silly things elsewhere. + # + # @api private + def self.provides?(node, _resource) + super && node['platform_family'] == 'windows' + end + + private + + def unpack_archive + notifying_block do + install_seven_zip + end + # Create a temp directory to unpack in to. Do I want to try and force + # this to be on the same filesystem as the target? + self.class.mktmpdir do |tmpdir| + unpack_using_seven_zip(tmpdir) + chown_files(tmpdir) if new_resource.user || new_resource.group + move_files(tmpdir) + end + end + + # Install 7-Zip to a cache folder. + # + # @api private + # @return [void] + def install_seven_zip + url = seven_zip_url + path = "#{Chef::Config[:file_cache_path]}/#{url.split(/\//).last}" + + install = execute "#{windows_path(path)} /S /D=#{seven_zip_home}" do + action :nothing + end + + remote_file path do + source url + notifies :run, install, :immediately + end + end + + # Unpack the whole archive to a temp directory. + # + # @api private + # @param tmpdir [String] Temp directory to unpack to. + # @return [void] + def unpack_using_seven_zip(tmpdir) + if new_resource.absolute_path =~ /\.t(ar\.)?(gz|bz(2)?|xz)$/ + # 7-Zip doesn't know to unpack both levels of the archive on its own + # so we need to handle this more explicitly. + shell_out!("#{seven_zip_home}\\7z.exe x -so \"#{windows_path(new_resource.absolute_path)}\" | #{seven_zip_home}\\7z.exe x -si -ttar -o\"#{windows_path(tmpdir)}\"") + else + shell_out!("#{seven_zip_home}\\7z.exe x -o\"#{windows_path(tmpdir)}\" \"#{windows_path(new_resource.absolute_path)}\"") + end + end + + # Fix file ownership if requested. + # + # @api private + # @param tmpdir [String] Temp directory to change ownership in. + # @return [void] + def chown_files(tmpdir) + notifying_block do + Dir["#{tmpdir}/**/*"].each do |path| + declare_resource(::File.directory?(path) ? :directory : :file, path) do + owner new_resource.user if new_resource.user + group new_resource.group if new_resource.group + end + end + end + end + + # Manual implementation of --strip-components since 7-Zip doesn't support + # it internally. + # + # @api private + # @param tmpdir [String] Temp directory to move from. + # @return [void] + def move_files(tmpdir) + entries_at_depth(tmpdir, new_resource.strip_components).each do |source| + target = ::File.join(new_resource.destination, ::File.basename(source)) + # If we are in keep_existing mode, the target might exist already. + # This is not a great solution and won't have exactly the same behavior + # as the other providers, but it's something at least. + FileUtils.rm_rf(target) if ::File.exist?(target) + # At some point this might need to fall back to a real copy. + ::File.rename(source, target) + end + end + + # Compute the URL to download the 7-Zip installer from. + # + # @api private + # @return [String] + def seven_zip_url + node['poise-archive']['seven_zip']['url'] % { + version: node['poise-archive']['seven_zip']['version'], + version_tag: node['poise-archive']['seven_zip']['version'].gsub(/\./, ''), + arch: node['kernel']['machine'], + arch_tag: node['kernel']['machine'] == 'x86_64' ? '-x64' : '', + } + end + + # Path to install 7-Zip in to. + # + # @api private + # @return [String] + def seven_zip_home + "#{windows_path(Chef::Config[:file_cache_path])}\\seven_zip_#{node['poise-archive']['seven_zip']['version']}" + end + + # Flip the slashes in a path because 7z wants "normal" paths. + # + # @api private + # @param path [String] Path to convert. + # @return [String] + def windows_path(path) + path.gsub(/\//, '\\') + end + + # Find the absolute paths for entries under a path at a depth. + # + # @api private + # @param path [String] Base path to search under. + # @param depth [Integer] Number of intermediary directories to skip. + # @return [Array] + def entries_at_depth(path, depth) + entries = [path] + current_depth = 0 + while current_depth <= depth + entries.map! do |ent| + if ::File.directory?(ent) + Dir.entries(ent).select {|e| e != '.' && e != '..' }.map {|e| ::File.join(ent, e) } + else + [] + end + end + entries.flatten! + current_depth += 1 + end + entries + end + + # Indirection so I can stub this for testing without breaking RSpec. + # + # @api private + def self.mktmpdir(*args, &block) + # :nocov: + Dir.mktmpdir(*args, &block) + # :nocov: + end + + end + end +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/tar.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/tar.rb new file mode 100644 index 0000000..50c3643 --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/tar.rb @@ -0,0 +1,158 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'rubygems/package' +require 'zlib' + +require 'poise_archive/archive_providers/base' +require 'poise_archive/bzip2' + + +module PoiseArchive + module ArchiveProviders + # The `tar` provider class for `poise_archive` to install from tar archives. + # + # @see PoiseArchive::Resources::PoiseArchive::Resource + # @provides poise_archive + class Tar < Base + provides_extension(/\.t(ar|gz|bz)/) + + # Hack that GNU tar uses for paths over 100 bytes. + # + # @api private + # @see #unpack_tar + TAR_LONGLINK = '././@LongLink' + + private + + def unpack_archive + unpack_tar + chown_entries if new_resource.user || new_resource.group + end + + # Unpack the archive. + # + # @return [void] + def unpack_tar + @tar_entry_paths = [] + tar_each_with_longlink do |entry| + entry_name = entry.full_name.split(/\//).drop(new_resource.strip_components).join('/') + # If strip_components wiped out the name, don't process this entry. + next if entry_name.empty? + dest = ::File.join(new_resource.destination, entry_name) + if entry.directory? + Dir.mkdir(dest, entry.header.mode) + @tar_entry_paths << [:directory, dest] + elsif entry.file? + ::File.open(dest, 'wb', entry.header.mode) do |dest_f| + while buf = entry.read(4096) + dest_f.write(buf) + end + end + @tar_entry_paths << [:file, dest] + elsif entry.header.typeflag == '2' # symlink? is new in Ruby 2.0, apparently. + ::File.symlink(entry.header.linkname, dest) + @tar_entry_paths << [:link, dest] + else + raise RuntimeError.new("Unknown tar entry type #{entry.header.typeflag.inspect} in #{new_resource.path}") + end + end + end + + def tar_each_with_longlink(&block) + entry_name = nil + tar_each do |entry| + if entry.full_name == TAR_LONGLINK + # Stash the longlink name so it will be used for the next entry. + entry_name = entry.read.strip + # And then skip forward because this isn't a real block. + next + end + # For entries not preceded by a longlink block, use the normal name. + entry_name ||= entry.full_name + # Make the entry return the correct name. + entry.define_singleton_method(:full_name) { entry_name } + block.call(entry) + # Reset entry_name for the next entry. + entry_name = nil + end + end + + # Sequence the opening, iteration, and closing. + # + # @param block [Proc] Block to process each tar entry. + # @return [void] + def tar_each(&block) + # In case of extreme weirdness where this happens twice. + close_file! + open_file! + @tar_reader.each(&block) + ensure + close_file! + end + + # Open a file handle of the correct flavor. + # + # @return [void] + def open_file! + @raw_file = ::File.open(new_resource.absolute_path, 'rb') + @file = case new_resource.absolute_path + when /\.tar$/ + nil # So it uses @raw_file instead. + when /\.t?gz/ + Zlib::GzipReader.wrap(@raw_file) + when /\.t?bz/ + # This can't take a block, hence the gross non-block forms for everything. + PoiseArchive::Bzip2::Decompressor.new(@raw_file) + else + raise RuntimeError.new("Unknown or unsupported file extension for #{new_resource.path}") + end + @tar_reader = Gem::Package::TarReader.new(@file || @raw_file) + end + + # Close all the various file handles. + # + # @return [void] + def close_file! + if @tar_reader + @tar_reader.close + @tar_reader = nil + end + if @file + @file.close + @file = nil + end + if @raw_file + @raw_file.close unless @raw_file.closed? + @raw_file = nil + end + end + + def chown_entries + paths = @tar_entry_paths + notifying_block do + paths.each do |type, path| + send(type, path) do + group new_resource.group + owner new_resource.user + end + end + end + end + + end + end +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/zip.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/zip.rb new file mode 100644 index 0000000..2dba06d --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/archive_providers/zip.rb @@ -0,0 +1,97 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_archive/archive_providers/base' + + +module PoiseArchive + module ArchiveProviders + # The `zip` provider class for `poise_archive` to install from ZIP archives. + # + # @see PoiseArchive::Resources::PoiseArchive::Resource + # @provides poise_archive + class Zip < Base + provides_extension(/\.zip$/) + + private + + def unpack_archive + check_rubyzip + unpack_zip + chown_entries if new_resource.user || new_resource.group + end + + def check_rubyzip + require 'zip' + rescue LoadError + notifying_block do + install_rubyzip + end + require 'zip' + end + + def install_rubyzip + chef_gem 'rubyzip' + end + + def unpack_zip + @zip_entry_paths = [] + ::Zip::File.open(new_resource.absolute_path) do |zip_file| + zip_file.each do |entry| + entry_name = entry.name.split(/\//).drop(new_resource.strip_components).join('/') + # If strip_components wiped out the name, don't process this entry. + next if entry_name.empty? + entry_path = ::File.join(new_resource.destination, entry_name) + # Ensure parent directories exist because some ZIP files don't + # include those for some reason. + ensure_directory(entry_path) + entry.extract(entry_path) + # Make sure we restore file permissions. RubyZip won't do this + # unless we also turn on UID/GID restoration, which we don't want. + # Mask filters out setuid and setgid bits because no. + ::File.chmod(entry.unix_perms & 01777, entry_path) if !node.platform_family?('windows') && entry.unix_perms + @zip_entry_paths << [entry.directory? ? :directory : entry.file? ? :file : :link, entry_path] + end + end + end + + # Make sure all enclosing directories exist before writing a path. + # + # @param oath [String] Path to check. + def ensure_directory(path) + base = ::File.dirname(path) + unless ::File.exist?(base) + ensure_directory(base) + Dir.mkdir(base) + @zip_entry_paths << [:directory, base] + end + end + + def chown_entries + paths = @zip_entry_paths + notifying_block do + paths.each do |type, path| + send(type, path) do + group new_resource.group + owner new_resource.user + end + end + end + end + + end + end +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2.rb new file mode 100644 index 0000000..4e1ecfc --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2.rb @@ -0,0 +1,16 @@ +# This code is free software; you can redistribute it and/or modify it under +# the terms of the new BSD License. +# +# Copyright (c) 2013, Sebastian Staudt + + +module PoiseArchive::Bzip2 + + autoload :CRC, 'poise_archive/bzip2/crc' + autoload :Constants, 'poise_archive/bzip2/constants' + autoload :Decompressor, 'poise_archive/bzip2/decompressor' + autoload :IO, 'poise_archive/bzip2/io' + autoload :InputData, 'poise_archive/bzip2/input_data' + autoload :OutputData, 'poise_archive/bzip2/output_data' + +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/LICENSE b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/LICENSE new file mode 100644 index 0000000..ba9a1a4 --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2011, Sebastian Staudt +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/constants.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/constants.rb new file mode 100644 index 0000000..102c620 --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/constants.rb @@ -0,0 +1,83 @@ +# This code is free software; you can redistribute it and/or modify it under +# the terms of the new BSD License. +# +# Copyright (c) 2011-2013, Sebastian Staudt + + +module PoiseArchive::Bzip2::Constants + + BASEBLOCKSIZE = 100000 + MAX_ALPHA_SIZE = 258 + MAX_CODE_LEN = 23 + RUNA = 0 + RUNB = 1 + N_GROUPS = 6 + G_SIZE = 50 + N_ITERS = 4 + MAX_SELECTORS = (2 + (900000 / G_SIZE)) + NUM_OVERSHOOT_BYTES = 20 + + EOF = 0 + START_BLOCK_STATE = 1 + RAND_PART_A_STATE = 2 + RAND_PART_B_STATE = 3 + RAND_PART_C_STATE = 4 + NO_RAND_PART_A_STATE = 5 + NO_RAND_PART_B_STATE = 6 + NO_RAND_PART_C_STATE = 7 + + RNUMS = [ + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 985, 724, 205, 454, 863, + 491, 741, 242, 949, 214, 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, 878, 465, 811, 169, 869, + 675, 611, 697, 867, 561, 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, 170, 607, 520, 932, 727, + 476, 693, 425, 174, 647, 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, 641, 801, 220, 162, 819, + 984, 589, 513, 495, 799, 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, 98, 553, 163, 354, 666, + 933, 424, 341, 533, 870, 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, 184, 943, 795, 384, 383, + 461, 404, 758, 839, 887, 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, 652, 934, 970, 447, 318, + 353, 859, 672, 112, 785, 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, 653, 282, 762, 623, 680, + 81, 927, 626, 789, 125, 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, 857, 956, 358, 619, 580, + 124, 737, 594, 701, 612, 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, 344, 805, 988, 739, 511, + 655, 814, 334, 249, 515, 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, 686, 754, 806, 760, 493, + 403, 415, 394, 687, 700, 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, 680, 879, 194, 572, 640, + 724, 926, 56, 204, 700, 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, 134, 108, 571, 364, 631, + 212, 174, 643, 304, 329, 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, 170, 514, 364, 692, 829, + 82, 855, 953, 676, 246, 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, 896, 831, 547, 261, 524, + 462, 293, 465, 502, 56, 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, 61, 688, 793, 644, 986, + 403, 106, 366, 905, 644, 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, 920, 176, 193, 713, 857, + 265, 203, 50, 668, 108, 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 + ] + + MIN_BLOCK_SIZE = 1 + MAX_BLOCK_SIZE = 9 + SETMASK = (1 << 21) + CLEARMASK = (~SETMASK) + GREATER_ICOST = 15 + LESSER_ICOST = 0 + SMALL_THRESH = 20 + DEPTH_THRESH = 10 + WORK_FACTOR = 30 + QSORT_STACK_SIZE = 1000 + + INCS = [ + 1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 797161, + 2391484 + ] + +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/crc.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/crc.rb new file mode 100644 index 0000000..c09f71d --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/crc.rb @@ -0,0 +1,73 @@ +# This code is free software; you can redistribute it and/or modify it under +# the terms of the new BSD License. +# +# Copyright (c) 2011-2013, Sebastian Staudt + + +class PoiseArchive::Bzip2::CRC + + CRC32_TABLE = [ + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + ] + + attr_accessor :global_crc + + def initialize + initialize_crc + end + + def initialize_crc + @global_crc = 0xffffffff + end + + def final_crc + @global_crc ^ 0xffffffff + end + + def update_crc(in_ch) + @global_crc = ((@global_crc << 8) & 0xffffffff) ^ CRC32_TABLE[(@global_crc >> 24) ^ in_ch] + end + +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/decompressor.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/decompressor.rb new file mode 100644 index 0000000..201fa3e --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/decompressor.rb @@ -0,0 +1,704 @@ +# This code is free software; you can redistribute it and/or modify it under +# the terms of the new BSD License. +# +# Copyright (c) 2011-2013, Sebastian Staudt + + +class PoiseArchive::Bzip2::Decompressor + + include PoiseArchive::Bzip2::Constants + + def initialize(io) + @buff = 0 + @bytes_read = 0 + @computed_combined_crc = 0 + @crc = PoiseArchive::Bzip2::CRC.new + @current_char = -1 + @io = io + @live = 0 + @stored_combined_crc = 0 + @su_t_pos = 0 + init + end + + def count(read) + @bytes_read += read if read != -1 + end + + # ADDED METHODS + def pos + @bytes_read + end + + def eof? + @current_state == EOF + end + # /ADDED METHODS + + def read(length = nil) + raise 'stream closed' if @io.nil? + + if length == 1 + r = read0 + count (r < 0 ? -1 : 1) + r + else + r = '' + if length == nil + while true do + b = read0 + break if b < 0 + r << b.chr + end + count r.size # ADDED LINE + elsif length > 0 + length.times do + b = read0 + break if b < 0 + r << b.chr + end + count r.size + end + r + end + end + + def read0 + ret_char = @current_char + + if @current_state == RAND_PART_B_STATE + setup_rand_part_b + elsif @current_state == NO_RAND_PART_B_STATE + setup_no_rand_part_b + elsif @current_state == RAND_PART_C_STATE + setup_rand_part_c + elsif @current_state == NO_RAND_PART_C_STATE + setup_no_rand_part_c + elsif @current_state == EOF + return -1 + else + raise 'illegal state' + end + + ret_char + end + + def make_maps + in_use = @data.in_use + seq_to_unseq = @data.seq_to_unseq + + n_in_use_shadow = 0 + + 256.times do |i| + if in_use[i] + seq_to_unseq[n_in_use_shadow] = i + n_in_use_shadow += 1 + end + end + + @n_in_use = n_in_use_shadow + end + + def init + check_magic + + block_size = @io.read(1).to_i + raise 'Illegal block size.' if block_size < 1 || block_size > 9 + @block_size = block_size + + init_block + setup_block + end + + def check_magic + raise 'Magic number does not match "BZh".' unless @io.read(3) == 'BZh' + end + + def init_block + magic = [ubyte, ubyte, ubyte, ubyte, ubyte, ubyte] + + if magic == [0x17, 0x72, 0x45, 0x38, 0x50, 0x90] + complete + elsif magic != [0x31, 0x41, 0x59, 0x26, 0x53, 0x59] + @current_state = EOF + + raise 'Bad block header.' + else + @stored_block_crc = int + @block_randomised = bit + + @data = PoiseArchive::Bzip2::InputData.new @block_size if @data.nil? + + get_and_move_to_front_decode + + @crc.initialize_crc + @current_state = START_BLOCK_STATE + end + end + + def end_block + @computed_block_crc = @crc.final_crc + + if @stored_block_crc != @computed_block_crc + @computed_combined_crc = (@stored_combined_crc << 1) | (@stored_combined_crc >> 31) + @computed_combined_crc ^= @stored_block_crc + + raise 'BZip2 CRC error' + end + + @computed_combined_crc = (@computed_combined_crc << 1) | (@computed_combined_crc >> 31) + @computed_combined_crc ^= @computed_block_crc + end + + def complete + @stored_combined_crc = int + @current_state = EOF + @data = nil + + raise 'BZip2 CRC error' if @stored_combined_crc != @computed_combined_crc + end + + def close + if @io != $stdin + @io = nil + @data = nil + end + end + + def r(n) + live_shadow = @live + buff_shadow = @buff + + if live_shadow < n + begin + thech = @io.readbyte + + raise 'unexpected end of stream' if thech < 0 + + buff_shadow = (buff_shadow << 8) | thech + live_shadow += 8 + end while live_shadow < n + + @buff = buff_shadow + end + + @live = live_shadow - n + + (buff_shadow >> (live_shadow - n)) & ((1 << n) - 1) + end + + def bit + r(1) != 0 + end + + def ubyte + r 8 + end + + def int + (((((r(8) << 8) | r(8)) << 8) | r(8)) << 8) | r(8) + end + + def create_decode_tables(limit, base, perm, length, min_len, max_len, alpha_size) + pp = 0 + (min_len..max_len).each do |i| + alpha_size.times do |j| + if length[j] == i + perm[pp] = j + pp += 1 + end + end + end + + MAX_CODE_LEN.downto 1 do |i| + base[i] = 0 + limit[i] = 0 + end + + alpha_size.times do |i| + base[length[i] + 1] += 1 + end + + b = 0 + 1.upto(MAX_CODE_LEN - 1) do |i| + b += base[i] + base[i] = b + end + + vec = 0 + min_len.upto(max_len) do |i| + b = base[i] + nb = base[i + 1] + vec += nb - b + b = nb + limit[i] = vec - 1 + vec = vec << 1 + end + + (min_len + 1).upto(max_len) do |i| + base[i] = ((limit[i - 1] + 1) << 1) - base[i] + end + end + + def receive_decoding_tables + in_use = @data.in_use + pos = @data.receive_decoding_tables_pos + selector = @data.selector + selector_mtf = @data.selector_mtf + + in_use16 = 0 + + 16.times do |i| + in_use16 |= 1 << i if bit + end + + 255.downto(0) do |i| + in_use[i] = false + end + + 16.times do |i| + if (in_use16 & (1 << i)) != 0 + i16 = i << 4 + 16.times do |j| + in_use[i16 + j] = true if bit + end + end + end + + make_maps + alpha_size = @n_in_use + 2 + + groups = r 3 + selectors = r 15 + + selectors.times do |i| + j = 0 + while bit + j += 1 + end + selector_mtf[i] = j + end + + groups.downto(0) do |v| + pos[v] = v + end + + selectors.times do |i| + v = selector_mtf[i] & 0xff + tmp = pos[v] + + while v > 0 do + pos[v] = pos[v -= 1] + end + + pos[0] = tmp + selector[i] = tmp + end + + len = @data.temp_char_array_2d + + groups.times do |t| + curr = r 5 + len_t = len[t] + alpha_size.times do |i| + while bit + curr += bit ? -1 : 1 + end + len_t[i] = curr + end + @data.temp_char_array_2d[t] = len_t + end + + create_huffman_decoding_tables alpha_size, groups + end + + def create_huffman_decoding_tables(alpha_size, groups) + len = @data.temp_char_array_2d + min_lens = @data.min_lens + limit = @data.limit + base = @data.base + perm = @data.perm + + groups.times do |t| + min_len = 32 + max_len = 0 + len_t = len[t] + + (alpha_size - 1).downto 0 do |i| + lent = len_t[i] + max_len = lent if lent > max_len + min_len = lent if lent < min_len + end + + create_decode_tables limit[t], base[t], perm[t], len[t], min_len, max_len, alpha_size + min_lens[t] = min_len + end + end + + def get_and_move_to_front_decode + @orig_ptr = r 24 + receive_decoding_tables + + ll8 = @data.ll8 + unzftab = @data.unzftab + selector = @data.selector + seq_to_unseq = @data.seq_to_unseq + yy = @data.get_and_move_to_front_decode_yy + min_lens = @data.min_lens + limit = @data.limit + base = @data.base + perm = @data.perm + limit_last = @block_size * BASEBLOCKSIZE + + 256.downto(0) do |i| + yy[i] = i + unzftab[i] = 0 + end + + group_no = 0 + group_pos = G_SIZE - 1 + eob = @n_in_use + 1 + next_sym = get_and_move_to_front_decode0 0 + buff_shadow = @buff + live_shadow = @live + last_shadow = -1 + zt = selector[group_no] & 0xff + base_zt = base[zt] + limit_zt = limit[zt] + perm_zt = perm[zt] + min_lens_zt = min_lens[zt] + + while next_sym != eob + if (next_sym == RUNA) || (next_sym == RUNB) + s = -1 + + n = 1 + while true do + if next_sym == RUNA + s += n + elsif next_sym == RUNB + s += n << 1 + else + break + end + + if group_pos == 0 + group_pos = G_SIZE - 1 + group_no += 1 + zt = selector[group_no] & 0xff + base_zt = base[zt] + limit_zt = limit[zt] + perm_zt = perm[zt] + min_lens_zt = min_lens[zt] + else + group_pos -= 1 + end + + zn = min_lens_zt + + while live_shadow < zn + thech = @io.readbyte + + raise 'unexpected end of stream' if thech < 0 + + buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech + live_shadow += 8 + end + + zvec = ((buff_shadow >> (live_shadow - zn)) & 0xffffffff) & ((1 << zn) - 1) + live_shadow -= zn + + while zvec > limit_zt[zn] + zn += 1 + + while live_shadow < 1 + thech = @io.readbyte + + raise 'unexpected end of stream' if thech < 0 + + buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech + live_shadow += 8 + end + + live_shadow -= 1 + zvec = (zvec << 1) | ((buff_shadow >> live_shadow) & 1) + end + + next_sym = perm_zt[zvec - base_zt[zn]] + + n = n << 1 + end + + ch = seq_to_unseq[yy[0]] + unzftab[ch & 0xff] += s + 1 + + while s >= 0 + last_shadow += 1 + ll8[last_shadow] = ch + s -= 1 + end + + raise 'block overrun' if last_shadow >= limit_last + else + last_shadow += 1 + raise 'block overrun' if last_shadow >= limit_last + + tmp = yy[next_sym - 1] + unzftab[seq_to_unseq[tmp] & 0xff] += 1 + ll8[last_shadow] = seq_to_unseq[tmp] + + yy[1, next_sym - 1] = yy[0, next_sym - 1] + yy[0] = tmp + + if group_pos == 0 + group_pos = G_SIZE - 1 + group_no += 1 + zt = selector[group_no] & 0xff + base_zt = base[zt] + limit_zt = limit[zt] + perm_zt = perm[zt] + min_lens_zt = min_lens[zt] + else + group_pos -= 1 + end + + zn = min_lens_zt + + while live_shadow < zn + thech = @io.readbyte + + raise 'unexpected end of stream' if thech < 0 + + buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech + live_shadow += 8 + end + zvec = (buff_shadow >> (live_shadow - zn)) & ((1 << zn) - 1) + live_shadow -= zn + + while zvec > limit_zt[zn] + zn += 1 + while live_shadow < 1 + thech = @io.readbyte + + raise 'unexpected end of stream' if thech < 0 + + buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech + live_shadow += 8 + end + live_shadow -= 1 + zvec = (zvec << 1) | ((buff_shadow >> live_shadow) & 1) + end + + next_sym = perm_zt[zvec - base_zt[zn]] + end + end + + @last = last_shadow + @live = live_shadow + @buff = buff_shadow + end + + def get_and_move_to_front_decode0(group_no) + zt = @data.selector[group_no] & 0xff + limit_zt = @data.limit[zt] + zn = @data.min_lens[zt] + zvec = r zn + live_shadow = @live + buff_shadow = @buff + + while zvec > limit_zt[zn] + zn += 1 + + while live_shadow < 1 + thech = @io.readbyte + + raise 'unexpected end of stream' if thech < 0 + + buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech + live_shadow += 8 + end + + live_shadow -=1 + zvec = (zvec << 1) | ((buff_shadow >> live_shadow) & 1) + end + + @live = live_shadow + @buff = buff_shadow + + @data.perm[zt][zvec - @data.base[zt][zn]] + end + + def setup_block + return if @data.nil? + + cftab = @data.cftab + tt = @data.init_tt @last + 1 + ll8 = @data.ll8 + cftab[0] = 0 + cftab[1, 256] = @data.unzftab[0, 256] + + c = cftab[0] + 1.upto(256) do |i| + c += cftab[i] + cftab[i] = c + end + + last_shadow = @last + (last_shadow + 1).times do |i| + cftab_i = ll8[i] & 0xff + tt[cftab[cftab_i]] = i + cftab[cftab_i] += 1 + end + + raise 'stream corrupted' if @orig_ptr < 0 || @orig_ptr >= tt.size + + @su_t_pos = tt[@orig_ptr] + @su_count = 0 + @su_i2 = 0 + @su_ch2 = 256 + + if @block_randomised + @su_r_n_to_go = 0 + @su_r_t_pos = 0 + + setup_rand_part_a + else + setup_no_rand_part_a + end + end + + def setup_rand_part_a + if @su_i2 <= @last + @su_ch_prev = @su_ch2 + su_ch2_shadow = @data.ll8[@su_t_pos] & 0xff + @su_t_pos = @data.tt[@su_t_pos] + + if @su_r_n_to_go == 0 + @su_r_n_to_go = RNUMS[@su_r_t_pos] - 1 + @su_r_t_pos += 1 + @su_r_t_pos = 0 if @su_r_t_pos == 512 + else + @su_r_n_to_go -= 1 + end + + @su_ch2 = su_ch2_shadow ^= (@su_r_n_to_go == 1) ? 1 : 0 + @su_i2 += 1 + @current_char = su_ch2_shadow + @current_state = RAND_PART_B_STATE + @crc.update_crc su_ch2_shadow + else + end_block + init_block + setup_block + end + end + + def setup_no_rand_part_a + if @su_i2 <= @last + @su_ch_prev = @su_ch2 + su_ch2_shadow = @data.ll8[@su_t_pos] & 0xff + @su_ch2 = su_ch2_shadow + @su_t_pos = @data.tt[@su_t_pos] + @su_i2 += 1 + @current_char = su_ch2_shadow + @current_state = NO_RAND_PART_B_STATE + @crc.update_crc su_ch2_shadow + else + @current_state = NO_RAND_PART_A_STATE + end_block + init_block + setup_block + end + end + + def setup_rand_part_b + if @su_ch2 != @su_ch_prev + @current_state = RAND_PART_A_STATE + @su_count = 1 + setup_rand_part_a + else + @su_count += 1 + if @su_count >= 4 + @su_z = @data.ll8[@su_t_pos] & 0xff + @su_t_pos = @data.tt[@su_t_pos] + + if @su_r_n_to_go == 0 + @su_r_n_to_go = RNUMS[@su_r_t_pos] - 1 + @su_r_t_pos += 1 + @su_r_t_pos = 0 if @su_r_t_pos == 512 + else + @su_r_n_to_go -= 1 + end + + @su_j2 = 0 + @current_state = RAND_PART_C_STATE + @su_z ^= 1 if @su_r_n_to_go == 1 + setup_rand_part_c + else + @current_state = RAND_PART_A_STATE + setup_rand_part_a + end + end + end + + def setup_rand_part_c + if @su_j2 < @su_z + @current_char = @su_ch2 + @crc.update_crc @su_ch2 + @su_j2 += 1 + else + @current_state = RAND_PART_A_STATE + @su_i2 += 1 + @su_count = 0 + setup_rand_part_a + end + end + + def setup_no_rand_part_b + if @su_ch2 != @su_ch_prev + @su_count = 1 + setup_no_rand_part_a + else + @su_count += 1 + if @su_count >= 4 + @su_z = @data.ll8[@su_t_pos] & 0xff + @su_t_pos = @data.tt[@su_t_pos] + @su_j2 = 0 + setup_no_rand_part_c + else + setup_no_rand_part_a + end + end + end + + def setup_no_rand_part_c + if @su_j2 < @su_z + su_ch2_shadow = @su_ch2 + @current_char = su_ch2_shadow + @crc.update_crc su_ch2_shadow + @su_j2 += 1 + @current_state = NO_RAND_PART_C_STATE + else + @su_i2 += 1 + @su_count = 0 + setup_no_rand_part_a + end + end + + def size + if @io.is_a? StringIO + @io.size + elsif @io.is_a? File + @io.stat.size + end + end + + def uncompressed + @last + 1 + end + + def inspect + "#<#{self.class}: @io=#{@io.inspect} size=#{size} uncompressed=#{uncompressed}>" + end + +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/input_data.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/input_data.rb new file mode 100644 index 0000000..a3ef7d2 --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/input_data.rb @@ -0,0 +1,43 @@ +# This code is free software; you can redistribute it and/or modify it under +# the terms of the new BSD License. +# +# Copyright (c) 2011-2013, Sebastian Staudt + + +class PoiseArchive::Bzip2::InputData + + include PoiseArchive::Bzip2::Constants + + attr_reader :base, :cftab, :get_and_move_to_front_decode_yy, :in_use, + :limit, :ll8, :min_lens, :perm, :receive_decoding_tables_pos, + :selector, :selector_mtf, :seq_to_unseq, :temp_char_array_2d, + :unzftab, :tt + + def initialize(block_size) + @in_use = Array.new 256, false + + @seq_to_unseq = Array.new 256, 0 + @selector = Array.new MAX_SELECTORS, 0 + @selector_mtf = Array.new MAX_SELECTORS, 0 + + @unzftab = Array.new 256, 0 + + @base = Array.new(N_GROUPS) { Array.new(MAX_ALPHA_SIZE, 0) } + @limit = Array.new(N_GROUPS) { Array.new(MAX_ALPHA_SIZE, 0) } + @perm = Array.new(N_GROUPS) { Array.new(MAX_ALPHA_SIZE, 0) } + @min_lens = Array.new N_GROUPS, 0 + + @cftab = Array.new 257, 0 + @get_and_move_to_front_decode_yy = Array.new 256 + @temp_char_array_2d = Array.new(N_GROUPS) { Array.new(MAX_ALPHA_SIZE, 0) } + @receive_decoding_tables_pos = Array.new N_GROUPS, 0 + + @ll8 = Array.new block_size * BASEBLOCKSIZE + end + + def init_tt(size) + @tt = Array.new(size) if @tt.nil? || @tt.size < size + @tt + end + +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/output_data.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/output_data.rb new file mode 100644 index 0000000..20f890f --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/bzip2/output_data.rb @@ -0,0 +1,57 @@ +# This code is free software; you can redistribute it and/or modify it under +# the terms of the new BSD License. +# +# Copyright (c) 2011-2013, Sebastian Staudt + + +class PoiseArchive::Bzip2::OutputData + + include PoiseArchive::Bzip2::Constants + + attr_reader :block, :ftab, :fmap, :generate_mtf_values_yy, :heap, :in_use, + :main_sort_big_done, :main_sort_copy, :main_sort_running_order, + :mtf_freq, :parent, :quadrant, :selector, :selector_mtf, + :send_mtf_values_code, :send_mtf_values_cost, + :send_mtf_values_fave, :send_mtf_values_len, + :send_mtf_values_rfreq, :send_mtf_values2_pos, + :send_mtf_values4_in_use_16, :sfmap, :stack_dd, :stack_hh, + :stack_ll, :unseq_to_seq, :weight + + def initialize(block_size) + n = block_size * BASEBLOCKSIZE + @block = Array.new n + 1 + NUM_OVERSHOOT_BYTES, 0 + @fmap = Array.new n, 0 + @selector = Array.new MAX_SELECTORS + @selector_mtf = Array.new MAX_SELECTORS + @sfmap = Array.new 2 * n + @quadrant = @sfmap + + @in_use = Array.new 256 + @mtf_freq = Array.new MAX_ALPHA_SIZE, 0 + @unseq_to_seq = Array.new 256 + + @generate_mtf_values_yy = Array.new 256 + @send_mtf_values_code = Array.new(N_GROUPS) { Array.new MAX_ALPHA_SIZE } + @send_mtf_values_cost = Array.new N_GROUPS + @send_mtf_values_fave = Array.new N_GROUPS + @send_mtf_values_len = Array.new(N_GROUPS) { Array.new MAX_ALPHA_SIZE } + @send_mtf_values_rfreq = Array.new(N_GROUPS) { Array.new MAX_ALPHA_SIZE } + @send_mtf_values2_pos = Array.new N_GROUPS + @send_mtf_values4_in_use_16 = Array.new 16 + + @stack_dd = Array.new QSORT_STACK_SIZE + @stack_hh = Array.new QSORT_STACK_SIZE + @stack_ll = Array.new QSORT_STACK_SIZE + + @main_sort_big_done = Array.new 256 + @main_sort_copy = Array.new 256 + @main_sort_running_order = Array.new 256 + + @heap = Array.new MAX_ALPHA_SIZE + 2 + @parent = Array.new MAX_ALPHA_SIZE + 2 + @weight = Array.new MAX_ALPHA_SIZE + 2 + + @ftab = Array.new 65537 + end + +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/cheftie.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/cheftie.rb new file mode 100644 index 0000000..cb3be2d --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/cheftie.rb @@ -0,0 +1,18 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_archive/resources' +require 'poise_archive/archive_providers' diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/resources.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/resources.rb new file mode 100644 index 0000000..d9ebb4a --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/resources.rb @@ -0,0 +1,26 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_archive/resources/poise_archive' + + +module PoiseArchive + # Chef resources and providers for poise-archive. + # + # @since 1.0.0 + module Resources + end +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/resources/poise_archive.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/resources/poise_archive.rb new file mode 100644 index 0000000..7186d6c --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/resources/poise_archive.rb @@ -0,0 +1,151 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'base64' +require 'uri' + +require 'chef/resource' +require 'poise' + + +module PoiseArchive + module Resources + # (see PoiseArchive::Resource) + # @since 1.0.0 + module PoiseArchive + # A `poise_archive` resource to unpack archives. + # + # @provides poise_archive + # @action unpack + # @example + # poise_archive '/opt/myapp.tgz' + # @example Downloading from a URL with options + # poise_archive ['http://example.com/myapp.zip', {headers: {'Authentication' => '...'}}] do + # destination '/opt/myapp' + # end + class Resource < Chef::Resource + include Poise + provides(:poise_archive) + actions(:unpack) + + # @!attribute path + # Path to the archive. If relative, it is taken as a file inside + # `Chef::Config[:file_cache_path]`. Can also be a URL to download the + # archive from. + # @return [String, Array] + attribute(:path, kind_of: String, default: lazy { @raw_name.is_a?(Array) ? @raw_name[0] : name }, required: true) + # @!attribute destination + # Path to unpack the archive to. If not specified, the path of the + # archive without the file extension is used. + # @return [String, nil, false] + attribute(:destination, kind_of: [String, NilClass, FalseClass], default: lazy { default_destination }) + # @!attribute group + # Group to run the unpack as. + # @return [String, Integer, nil, false] + attribute(:group, kind_of: [String, Integer, NilClass, FalseClass]) + # @!attribute keep_existing + # Keep existing files in the destination directory when unpacking. + # @return [Boolean] + attribute(:keep_existing, equal_to: [true, false], default: false) + # @!attribute source_properties + # Properties to pass through to the underlying download resource if + # using one. Merged with the array form of {#name}. + # @return [Hash] + attribute(:source_properties, option_collector: true, forced_keys: %i{retries}) + # @!attribute strip_components + # Number of intermediary directories to skip when unpacking. Works + # like GNU tar's --strip-components. + # @return [Integer] + attribute(:strip_components, kind_of: Integer, default: 1) + # @!attribute user + # User to run the unpack as. + # @return [String, Integer, nil, false] + attribute(:user, kind_of: [String, Integer, NilClass, FalseClass]) + + # Alias for the forgetful. + # @api private + alias_method :owner, :user + + def initialize(name, run_context) + @raw_name = name # Capture this before it gets coerced to a string. + super + end + + # Regexp for URL-like paths. + # @api private + URL_PATHS = %r{^(\w+:)?//} + + # Check if the source path is a URL. + # + # @api private + # @return [Boolean] + def is_url? + path =~ URL_PATHS + end + + # Expand a relative file path against `Chef::Config[:file_cache_path]`. + # For URLs it returns the cache file path. + # + # @api private + # @return [String] + def absolute_path + if is_url? + # Use the last path component without the query string plus the name + # of the resource in Base64. This should be both mildly readable and + # also unique per invocation. + url_part = URI(path).path.split(/\//).last + base64_name = Base64.strict_encode64(name).gsub(/\=/, '') + ::File.join(Chef::Config[:file_cache_path], "#{base64_name}_#{url_part}") + else + ::File.expand_path(path, Chef::Config[:file_cache_path]) + end + end + + # Merge the explicit source properties with the array form of the name. + # + # @api private + # @return [Hash] + def merged_source_properties + if @raw_name.is_a?(Array) && @raw_name[1] + source_properties.merge(@raw_name[1]) + else + source_properties + end + end + + private + + # Filename components to ignore. + # @api private + BASENAME_IGNORE = /(\.(t?(ar|gz|bz2?|xz)|zip))+$/ + + # Default value for the {#destination} property + # + # @api private + # @return [String] + def default_destination + if is_url? + raise ValueError.new("Destination for URL-based archive #{self} must be specified explicitly") + else + ::File.join(::File.dirname(absolute_path), ::File.basename(path).gsub(BASENAME_IGNORE, '')) + end + end + end + + # Providers can be found in archive_providers/. + end + end +end diff --git a/cookbooks/poise-archive/files/halite_gem/poise_archive/version.rb b/cookbooks/poise-archive/files/halite_gem/poise_archive/version.rb new file mode 100644 index 0000000..252b210 --- /dev/null +++ b/cookbooks/poise-archive/files/halite_gem/poise_archive/version.rb @@ -0,0 +1,20 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseArchive + VERSION = '1.4.0' +end diff --git a/cookbooks/poise-archive/libraries/default.rb b/cookbooks/poise-archive/libraries/default.rb new file mode 100644 index 0000000..01175df --- /dev/null +++ b/cookbooks/poise-archive/libraries/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2016-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) +require "poise_archive/cheftie" diff --git a/cookbooks/poise-archive/metadata.json b/cookbooks/poise-archive/metadata.json new file mode 100644 index 0000000..151d5e8 --- /dev/null +++ b/cookbooks/poise-archive/metadata.json @@ -0,0 +1 @@ +{"name":"poise-archive","version":"1.4.0","description":"A Chef cookbook for unpacking file archives like tar and zip.","long_description":"# Poise-Archive Cookbook\n\n[![Build Status](https://img.shields.io/travis/poise/poise-archive.svg)](https://travis-ci.org/poise/poise-archive)\n[![Gem Version](https://img.shields.io/gem/v/poise-archive.svg)](https://rubygems.org/gems/poise-archive)\n[![Cookbook Version](https://img.shields.io/cookbook/v/poise-archive.svg)](https://supermarket.chef.io/cookbooks/poise-archive)\n[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-archive.svg)](https://codecov.io/github/poise/poise-archive)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-archive.svg)](https://gemnasium.com/poise/poise-archive)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nA [Chef](https://www.chef.io/) cookbook to unpack file archives.\n\nIt supports `.tar`, `.tar.gz`, `.tar.bz2`, and `.zip` archive files.\n\n## Quick Start\n\nTo download an unpack and archive:\n\n```ruby\npoise_archive 'https://example.com/myapp.tgz' do\n destination '/opt/myapp'\nend\n```\n\n## Requirements\n\nChef 12.1 or newer is required.\n\n## Platforms\n\nThis cookbook supports all platforms (including Windows) but some Unix platforms\n(Solaris, AIX) may see very slow tar file unpacking when using the pure-Ruby fallback\nimplementation.\n\n## Resources\n\n### `poise_archive`\n\nThe `poise_archive` resource unpacks file archives.\n\n```ruby\npoise_archive '/tmp/myapp-1.2.0.tar' do\n destination '/srv/myapp-1.2.0'\nend\n```\n\nA URL can also be passed as the source path, optionally with extra properties to\nbe merged with `source_properties`.\n\n```ruby\npoise_archive 'http://example.com/myapp-1.2.0.zip' do\n destination '/srv/myapp-1.2.0'\nend\n\npoise_archive ['http://example.com/myapp-1.2.0.zip', {headers: {'Authentication' => '...'}}] do\n destination '/srv/myapp-1.2.0'\nend\n```\n\n#### Actions\n\n* `:unpack` – Unpack the archive. *(default)*\n\n#### Properties\n\n* `path` – Path to the archive. If relative, it is taken as a file inside\n `Chef::Config[:file_cache_path]`. If a URL, it is downloaded to a cache file\n first. *(name attribute)*\n* `destination` – Path to unpack the archive to. If not specified, the path of\n the archive without the file extension is used. Required when unpacking from\n a URL. *(default: auto)*\n* `group` – Group to run the unpack as.\n* `keep_existing` – Keep existing files in the destination directory when\n unpacking. *(default: false)*\n* `source_properties` – Property key/value pairs to be applied to the\n `remote_file` file resource when downloading a URL. *(default: {retries: 5})*\n* `strip_components` – Number of intermediary directories to skip when\n unpacking. Works like GNU tar's `--strip-components`. *(default: 1)*\n* `user` – User to run the unpack as.\n\n## Sponsors\n\nDevelopment sponsored by [Bloomberg](http://www.bloomberg.com/company/technology/).\n\nThe Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/).\n\n## License\n\nCopyright 2016-2017, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\nBZip2 implementation is based on RBzip2. Copyright Sebastian Staudt, Brian Lopez.\nRBzip2 code used under the terms of the new BSD license.\n","maintainer":"Noah Kantrowitz","maintainer_email":"noah@coderanger.net","license":"Apache 2.0","platforms":{},"dependencies":{"poise":"~> 2.6"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{},"source_url":"https://github.com/poise/poise-archive","issues_url":"https://github.com/poise/poise-archive/issues","chef_version":"~> 12","ohai_version":{}} \ No newline at end of file diff --git a/cookbooks/poise-javascript/CHANGELOG.md b/cookbooks/poise-javascript/CHANGELOG.md new file mode 100644 index 0000000..0756dd1 --- /dev/null +++ b/cookbooks/poise-javascript/CHANGELOG.md @@ -0,0 +1,16 @@ +# Poise-Javascript Changelog + +## v1.1.0 + +* New version list for Node.js. +* Support new SCL structure and packages. + +## v1.0.1 + +* Update for Chef 12.6 compatibility. +* Update version list for `nodejs` provider. + +## v1.0.0 + +* Initial release! + diff --git a/cookbooks/poise-javascript/README.md b/cookbooks/poise-javascript/README.md new file mode 100644 index 0000000..ed7122f --- /dev/null +++ b/cookbooks/poise-javascript/README.md @@ -0,0 +1,332 @@ +# Poise-Javascript Cookbook + +[![Build Status](https://img.shields.io/travis/poise/poise-javascript.svg)](https://travis-ci.org/poise/poise-javascript) +[![Gem Version](https://img.shields.io/gem/v/poise-javascript.svg)](https://rubygems.org/gems/poise-javascript) +[![Cookbook Version](https://img.shields.io/cookbook/v/poise-javascript.svg)](https://supermarket.chef.io/cookbooks/poise-javascript) +[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-javascript.svg)](https://codecov.io/github/poise/poise-javascript) +[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-javascript.svg)](https://gemnasium.com/poise/poise-javascript) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +A [Chef](https://www.chef.io/) cookbook to provide a unified interface for +installing server-side JavaScript runtimes like Node.js and io.js. + +## Quick Start + +To install the latest available version of Node.js 0.12: + +```ruby +javascript_runtime '0.12' +``` + +## Supported JavaScript Runtimes + +This cookbook can install Node.js and io.js on Linux and OS X. + +## Requirements + +Chef 12.1 or newer is required. + +## Attributes + +Attributes are used to configure the default recipe. + +* `node['poise-javascript']['install_nodejs']` – Install a Node.js runtime. *(default: true)* +* `node['poise-javascript']['install_iojs']` – Install an io.js runtime. *(default: false)* + +## Recipes + +### `default` + +The default recipe installs Node.js or io.js based on the node attributes. It is +entirely optional and can be ignored in favor of direct use of the +`javascript_runtime` resource. + +## Resources + +### `javascript_runtime` + +The `javascript_runtime` resource installs a JavaScript interpreter. + +```ruby +javascript_runtime '0.12' +``` + +#### Actions + +* `:install` – Install the JavaScript interpreter. *(default)* +* `:uninstall` – Uninstall the JavaScript interpreter. + +#### Properties + +* `version` – Version of the runtime to install. If a partial version is given, + use the latest available version matching that prefix. *(name property)* + +#### Provider Options + +The `poise-javascript` library offers an additional way to pass configuration +information to the final provider called "options". Options are key/value pairs +that are passed down to the `javascript_runtime` provider and can be used to control how it +installs JavaScript. These can be set in the `javascript_runtime` +resource using the `options` method, in node attributes or via the +`javascript_runtime_options` resource. The options from all sources are merged +together in to a single hash. + +When setting options in the resource you can either set them for all providers: + +```ruby +javascript_runtime 'myapp' do + version '0.10' + options dev_package: false +end +``` + +or for a single provider: + +```ruby +javascript_runtime 'myapp' do + version '0.10' + options :system, dev_package: false +end +``` + +Setting via node attributes is generally how an end-user or application cookbook +will set options to customize installations in the library cookbooks they are using. +You can set options for all installations or for a single runtime: + +```ruby +# Global, for all installations. +override['poise-javascript']['options']['version'] = '0.10' +# Single installation. +override['poise-javascript']['myapp']['version'] = 'iojs' +``` + +The `javascript_runtime_options` resource is also available to set node attributes +for a specific installation in a DSL-friendly way: + +```ruby +javascript_runtime_options 'myapp' do + version 'iojs' +end +``` + +Unlike resource attributes, provider options can be different for each provider. +Not all providers support the same options so make sure to the check the +documentation for each provider to see what options the use. + +### `javascript_runtime_options` + +The `javascript_runtime_options` resource allows setting provider options in a +DSL-friendly way. See [the Provider Options](#provider-options) section for more +information about provider options overall. + +```ruby +javascript_runtime_options 'myapp' do + version 'iojs' +end +``` + +#### Actions + +* `:run` – Apply the provider options. *(default)* + +#### Properties + +* `resource` – Name of the `javascript_runtime` resource. *(name property)* +* `for_provider` – Provider to set options for. + +All other attribute keys will be used as options data. + +### `javascript_execute` + +The `javascript_execute` resource executes a JavaScript script using the configured runtime. + +```ruby +javascript_execute 'myapp.js' do + user 'myuser' +end +``` + +This uses the built-in `execute` resource and supports all the same properties. + +#### Actions + +* `:run` – Execute the script. *(default)* + +#### Properties + +* `command` – Script and arguments to run. Must not include the `node`. *(name attribute)* +* `javascript` – Name of the `javascript_runtime` resource to use. If not specified, the + most recently declared `javascript_runtime` will be used. Can also be set to the + full path to a `node` binary. + +For other properties see the [Chef documentation](https://docs.chef.io/resource_execute.html#attributes). + +### `node_package` + +The `node_package` resource installs Node.js packages using +[NPM](https://www.npmjs.com/). + +```ruby +node_package 'express' do + version '4.13.3' +end +``` + +This uses the built-in `package` resource and supports the same actions and +properties. Multi-package installs are supported using the standard syntax. + +#### Actions + +* `:install` – Install the package. *(default)* +* `:upgrade` – Upgrade the package. +* `:remove` – Uninstall the package. + +The `:purge` and `:reconfigure` actions are not supported. + +#### Properties + +* `group` – System group to install the package. +* `package_name` – Package or packages to install. *(name property)* +* `path` – Path to install the package in to. If unset install using `--global`. + *(default: nil)* +* `version` – Version or versions to install. +* `javascript` – Name of the `javascript_runtime` resource to use. If not specified, the + most recently declared `javascript_runtime` will be used. Can also be set to the + full path to a `node` binary. +* `unsafe_perm` – Enable `--unsafe-perm`. *(default: true)* +* `user` – System user to install the package. + +For other properties see the [Chef documentation](https://docs.chef.io/resource_package.html#attributes). +The `response_file`, `response_file_variables`, and `source` properties are not +supported. + +### `npm_install` + +The `npm_install` resource runs `npm install` for a package. + +```ruby +npm_install '/opt/myapp' +``` + +The underlying `npm install` command will run on every converge, but notifications +will only be triggered if a package is actually installed. + +#### Actions + +* `:install` – Run `npm install`. *(default)* + +#### Properties + +* `path` – Path to the package folder containing a `package.json`. *(name attribute)* +* `group` – System group to install the packages. +* `javascript` – Name of the `javascript_runtime` resource to use. If not specified, the + most recently declared `javascript_runtime` will be used. Can also be set to the + full path to a `node` binary. +* `production` – Enable production install mode. *(default: true)* +* `unsafe_perm` – Enable `--unsafe-perm`. *(default: true)* +* `user` – System user to install the packages. + +## Javascript Providers + +### Common Options + +These provider options are supported by all providers. + +* `version` – Override the runtime version. + +### `system` + +The `system` provider installs Node.js using system packages. This is currently +only tested on platforms using `apt-get` and `yum` (Debian, Ubuntu, RHEL, CentOS +Amazon Linux, and Fedora). It may work on other platforms but is untested. + +```ruby +javascript_runtime 'myapp' do + provider :system + version '0.10' +end +``` + +#### Options + +* `dev_package` – Install the package with the headers and other development + files. Can be set to a string to select the dev package specifically. + *(default: true)* +* `package_name` – Override auto-detection of the package name. +* `package_upgrade` – Install using action `:upgrade`. *(default: false)* +* `package_version` – Override auto-detection of the package version. + +### `scl` + +The `scl` provider installs Node.js using the [Software Collections](https://www.softwarecollections.org/) +packages. This is only available on RHEL and CentOS. SCL offers more +recent versions of Node.js than the system packages for the most part. If an SCL +package exists for the requests version, it will be used in preference to the +`system` provider. + +```ruby +javascript_runtime 'myapp' do + provider :scl + version '0.10' +end +``` + +### `nodejs` + +The `nodejs` provider installs Node.js from the static binaries on nodejs.org. +Support is included for Linux and OS X. + +```ruby +javascript_runtime 'myapp' do + provider :nodejs + version '0.12' +end +``` + +#### Options + +* `path` – Folder to install Node.js to. *(default: /opt/nodejs-)* +* `static_version` – Specific version number to use for computing the URL and + path. *(default: automatic from `version`)* +* `strip_components` – Value to pass to tar --strip-components. *(automatic)* +* `url` – URL template to download the archive from. *(default: automatic)* + +### `iojs` + +The `iojs` provider installs io.js from the static binaries on iojs.org. +Support is included for Linux and OS X. + +```ruby +javascript_runtime 'myapp' do + provider :iojs + version '3' +end +``` + +#### Options + +* `path` – Folder to install io.js to. *(default: /opt/iojs-)* +* `static_version` – Specific version number to use for computing the URL and + path. *(default: automatic from `version`)* +* `strip_components` – Value to pass to tar --strip-components. *(automatic)* +* `url` – URL template to download the archive from. *(default: automatic)* + +## Sponsors + +The Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/). + +## License + +Copyright 2015-2016, Noah Kantrowitz + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cookbooks/poise-javascript/attributes/default.rb b/cookbooks/poise-javascript/attributes/default.rb new file mode 100644 index 0000000..a72ea0f --- /dev/null +++ b/cookbooks/poise-javascript/attributes/default.rb @@ -0,0 +1,23 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Default inversion options. +default['poise-javascript']['provider'] = 'auto' +default['poise-javascript']['options'] = {} + +# Used for the default recipe. +default['poise-javascript']['install_nodejs'] = true +default['poise-javascript']['install_iojs'] = false diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript.rb new file mode 100644 index 0000000..b91e3e7 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript.rb @@ -0,0 +1,24 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseJavascript + autoload :Error, 'poise_javascript/error' + autoload :Resources, 'poise_javascript/resources' + autoload :JavascriptCommandMixin, 'poise_javascript/javascript_command_mixin' + autoload :JavascriptProviders, 'poise_javascript/javascript_providers' + autoload :VERSION, 'poise_javascript/version' +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/cheftie.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/cheftie.rb new file mode 100644 index 0000000..d76a171 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/cheftie.rb @@ -0,0 +1,18 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_javascript/resources' +require 'poise_javascript/javascript_providers' diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/error.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/error.rb new file mode 100644 index 0000000..d8c6eb2 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/error.rb @@ -0,0 +1,23 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_languages' + + +module PoiseJavascript + class Error < PoiseLanguages::Error + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_command_mixin.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_command_mixin.rb new file mode 100644 index 0000000..cd7297d --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_command_mixin.rb @@ -0,0 +1,56 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/utils' +require 'poise_languages' + + +module PoiseJavascript + # Mixin for resources and providers which run Javascript commands. + # + # @since 1.0.0 + module JavascriptCommandMixin + include Poise::Utils::ResourceProviderMixin + + # Mixin for resources which run Javascript commands. + module Resource + include PoiseLanguages::Command::Mixin::Resource(:javascript, default_binary: 'node') + + # @!attribute npm_binary + # Path to the npm binary. + # @return [String] + attribute(:npm_binary, kind_of: String, default: lazy { default_npm_binary }) + + private + + # Find the default gem binary. If there is a parent use that, otherwise + # use the same logic as {PoiseRuby::RubyProviders::Base#npm_binary}. + # + # @return [String] + def default_npm_binary + if parent_javascript + parent_javascript.npm_binary + else + ::File.expand_path('../npm', javascript) + end + end + end + + module Provider + include PoiseLanguages::Command::Mixin::Provider(:javascript) + end + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers.rb new file mode 100644 index 0000000..6855d6a --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers.rb @@ -0,0 +1,40 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/platform/provider_priority_map' + +require 'poise_javascript/javascript_providers/dummy' +require 'poise_javascript/javascript_providers/iojs' +require 'poise_javascript/javascript_providers/nodejs' +require 'poise_javascript/javascript_providers/scl' +require 'poise_javascript/javascript_providers/system' + + +module PoiseJavascript + # Inversion providers for the javascript_runtime resource. + # + # @since 1.0.0 + module JavascriptProviders + autoload :Base, 'poise_javascript/javascript_providers/base' + + Chef::Platform::ProviderPriorityMap.instance.priority(:javascript_runtime, [ + PoiseJavascript::JavascriptProviders::IOJS, + PoiseJavascript::JavascriptProviders::NodeJS, + PoiseJavascript::JavascriptProviders::Scl, + PoiseJavascript::JavascriptProviders::System, + ]) + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/base.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/base.rb new file mode 100644 index 0000000..ddd261e --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/base.rb @@ -0,0 +1,97 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'poise' + + +module PoiseJavascript + module JavascriptProviders + class Base < Chef::Provider + include Poise(inversion: :javascript_runtime) + + # Set default inversion options. + # + # @api private + def self.default_inversion_options(node, new_resource) + super.merge({ + version: new_resource.version, + }) + end + + # The `install` action for the `javascript_runtime` resource. + # + # @return [void] + def action_install + notifying_block do + install_javascript + end + end + + # The `uninstall` action for the `javascript_runtime` resource. + # + # @abstract + # @return [void] + def action_uninstall + notifying_block do + uninstall_javascript + end + end + + # The path to the `javascript` binary. This is an output property. + # + # @abstract + # @return [String] + def javascript_binary + raise NotImplementedError + end + + # The environment variables for this Javascript. This is an output property. + # + # @return [Hash] + def javascript_environment + {} + end + + # The path to the `npm` binary. This is an output property. + # + # @abstract + # @return [String] + def npm_binary + ::File.expand_path(::File.join('..', 'npm'), javascript_binary) + end + + private + + # Install the Javascript runtime. Must be implemented by subclass. + # + # @abstract + # @return [void] + def install_javascript + raise NotImplementedError + end + + # Uninstall the Javascript runtime. Must be implemented by subclass. + # + # @abstract + # @return [void] + def uninstall_javascript + raise NotImplementedError + end + + end + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/dummy.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/dummy.rb new file mode 100644 index 0000000..6dcbd50 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/dummy.rb @@ -0,0 +1,77 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_javascript/javascript_providers/base' + + +module PoiseJavascript + module JavascriptProviders + # Inversion provider for the `javascript_runtime` resource to use a fake Javascript, + # for use in unit tests. + # + # @since 1.0.0 + # @provides dummy + class Dummy < Base + provides(:dummy) + + def self.default_inversion_options(node, resource) + super.merge({ + # Manual overrides for dummy data. + javascript_binary: ::File.join('', 'node'), + javascript_environment: nil, + npm_binary: nil, + }) + end + + # The `install` action for the `javascript_runtime` resource. + # + # @return [void] + def action_install + # This space left intentionally blank. + end + + # The `uninstall` action for the `javascript_runtime` resource. + # + # @return [void] + def action_uninstall + # This space left intentionally blank. + end + + # Path to the non-existent Javascript. + # + # @return [String] + def javascript_binary + options['javascript_binary'] + end + + # Environment for the non-existent Javascript. + # + # @return [String] + def javascript_environment + options['javascript_environment'] || super + end + + # Path to the non-existent npm. + # + # @return [String] + def npm_binary + options['npm_binary'] || super + end + + end + end +end + diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/iojs.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/iojs.rb new file mode 100644 index 0000000..236679c --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/iojs.rb @@ -0,0 +1,64 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'poise_languages/static' + +require 'poise_javascript/error' +require 'poise_javascript/javascript_providers/base' + + +module PoiseJavascript + module JavascriptProviders + class IOJS < Base + provides(:iojs) + include PoiseLanguages::Static( + versions: %w{3.3.1 3.2.0 3.1.0 3.0.0 2.5.0 2.4.0 2.3.4 2.2.1 2.1.0 2.0.2 1.8.4 1.7.1 1.6.4 1.5.1 1.4.3 1.3.0 1.2.0 1.1.0 1.0.4}, + machines: %w{linux-i686 linux-x86_64 darwin-x86_64}, + url: 'https://iojs.org/dist/v%{version}/iojs-v%{version}-%{kernel}-%{machine}.tar.gz', + ) + + def self.provides_auto?(node, resource) + # Also work if we have a version starting with 1. 2. or 3. since that has + # to be io.js and no other mechanism supports that. + super || (resource.version.to_s =~ /^[123](\.|$)/ && static_machines.include?(static_machine_label(node))) + end + + MACHINE_LABELS = {'i386' => 'x86', 'i686' => 'x86', 'x86_64' => 'x64'} + + def static_url_variables + machine = node['kernel']['machine'] + super.merge(machine: MACHINE_LABELS[machine] || machine) + end + + def javascript_binary + ::File.join(static_folder, 'bin', 'iojs') + end + + private + + def install_javascript + install_static + end + + def uninstall_javascript + uninstall_static + end + + end + end +end + diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/nodejs.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/nodejs.rb new file mode 100644 index 0000000..06dd54d --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/nodejs.rb @@ -0,0 +1,65 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'poise_languages/static' + +require 'poise_javascript/error' +require 'poise_javascript/javascript_providers/base' + + +module PoiseJavascript + module JavascriptProviders + class NodeJS < Base + provides(:nodejs) + include PoiseLanguages::Static( + # 4.x is still first here because that is what NodeJS recommends. + versions: %w{4.5.0 6.4.0 6.3.1 6.2.2 6.1.0 6.0.0 5.11.0 5.10.1 5.9.1 5.8.0 5.7.1 5.6.0 5.5.0 5.4.1 5.3.0 5.2.0 5.1.1 5.0.0 4.4.3 4.3.2 4.2.6 4.1.1 4.0.0 0.12.7 0.11.16 0.10.40 0.9.12 0.8.28 0.7.12 0.6.21 0.5.10}, + machines: %w{linux-i686 linux-x86_64 linux-armv6l linux-armv7l linux-arm64 darwin-x86_64}, + url: 'https://nodejs.org/dist/v%{version}/node-v%{version}-%{kernel}-%{machine}.tar.gz', + ) + + def self.provides_auto?(node, resource) + # Also work if we have a blank or numeric-y version. This should make + # it the default provider on supported platforms. + super || (resource.version.to_s =~ /^(\d|$)/ && static_machines.include?(static_machine_label(node))) + end + + MACHINE_LABELS = {'i386' => 'x86', 'i686' => 'x86', 'x86_64' => 'x64'} + + def static_url_variables + machine = node['kernel']['machine'] + super.merge(machine: MACHINE_LABELS[machine] || machine) + end + + def javascript_binary + ::File.join(static_folder, 'bin', 'node') + end + + private + + def install_javascript + install_static + end + + def uninstall_javascript + uninstall_static + end + + end + end +end + diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/scl.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/scl.rb new file mode 100644 index 0000000..268df48 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/scl.rb @@ -0,0 +1,53 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'poise_languages' + +require 'poise_javascript/error' +require 'poise_javascript/javascript_providers/base' + + +module PoiseJavascript + module JavascriptProviders + class Scl < Base + include PoiseLanguages::Scl::Mixin + provides(:scl) + scl_package('4.4.2', 'rh-nodejs4', 'rh-nodejs4-nodejs-devel', '>= 7.0') + scl_package('0.10.35', 'nodejs010', 'nodejs010-nodejs-devel') + + def javascript_binary + ::File.join(scl_folder, 'root', 'usr', 'bin', 'node') + end + + def javascript_environment + scl_environment + end + + private + + def install_javascript + install_scl_package + end + + def uninstall_javascript + uninstall_scl_package + end + + end + end +end + diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/system.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/system.rb new file mode 100644 index 0000000..e0eca2f --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/javascript_providers/system.rb @@ -0,0 +1,71 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'poise_languages' + +require 'poise_javascript/error' +require 'poise_javascript/javascript_providers/base' + + +module PoiseJavascript + module JavascriptProviders + class System < Base + include PoiseLanguages::System::Mixin + provides(:system) + packages('nodejs', { + debian: {default: %w{nodejs}}, + ubuntu: {default: %w{nodejs}}, + # Empty arrays because no package in the base OS. + redhat: {default: %w{}}, + centos: {default: %w{}}, + fedora: {default: %w{nodejs}}, + amazon: {default: %w{}}, + }) + + def self.provides_auto?(node, resource) + # Don't auto on platforms I know have no system package by default. Kind + # of pointless since the nodejs provider will hit on these platforms + # anyway so this shouldn't ever happen. + super && !node.platform_family?('rhel') && !node.platform?('amazon') + end + + def javascript_binary + # Debian and Ubuntu after 12.04 changed the binary name ಠ_ಠ. + binary_name = node.value_for_platform(debian: {default: 'nodejs'}, ubuntu: {'12.04' => 'node', default: 'nodejs'}, default: 'node') + ::File.join('', 'usr', 'bin', binary_name) + end + + private + + def install_javascript + install_system_packages + package %w{npm nodejs-legacy} if node.platform_family?('debian') + end + + def uninstall_javascript + uninstall_system_packages + package(%w{npm nodejs-legacy}) { action :purge } if node.platform_family?('debian') + end + + def system_package_candidates(version) + # Boring :-(. + %w{nodejs nodejs-legacy node} + end + + end + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources.rb new file mode 100644 index 0000000..609e164 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources.rb @@ -0,0 +1,29 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_javascript/resources/javascript_execute' +require 'poise_javascript/resources/javascript_runtime' +require 'poise_javascript/resources/node_package' +require 'poise_javascript/resources/npm_install' + + +module PoiseJavascript + # Chef resources and providers for poise-javascript. + # + # @since 1.0.0 + module Resources + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/javascript_execute.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/javascript_execute.rb new file mode 100644 index 0000000..143b085 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/javascript_execute.rb @@ -0,0 +1,83 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/which' +require 'chef/provider/execute' +require 'chef/resource/execute' +require 'poise' + +require 'poise_javascript/javascript_command_mixin' + + +module PoiseJavascript + module Resources + # (see JavascriptExecute::Resource) + # @since 1.0.0 + module JavascriptExecute + # A `javascript_execute` resource to run Javascript scripts and commands. + # + # @provides javascript_execute + # @action run + # @example + # javascript_execute 'myapp.js' do + # user 'myuser' + # end + class Resource < Chef::Resource::Execute + include PoiseJavascript::JavascriptCommandMixin + provides(:javascript_execute) + actions(:run) + end + + # The default provider for `javascript_execute`. + # + # @see Resource + # @provides javascript_execute + class Provider < Chef::Provider::Execute + include Chef::Mixin::Which + provides(:javascript_execute) + + private + + # Command to pass to shell_out. + # + # @return [String, Array] + def command + if new_resource.command.is_a?(Array) + [new_resource.javascript] + new_resource.command + else + "#{new_resource.javascript} #{new_resource.command}" + end + end + + # Environment variables to pass to shell_out. + # + # @return [Hash] + def environment + if new_resource.parent_javascript + environment = new_resource.parent_javascript.javascript_environment + if new_resource.environment + environment = environment.merge(new_resource.environment) + end + environment + else + new_resource.environment + end + end + + end + end + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/javascript_runtime.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/javascript_runtime.rb new file mode 100644 index 0000000..ff5183a --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/javascript_runtime.rb @@ -0,0 +1,85 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'poise' + + +module PoiseJavascript + module Resources + # (see JavascriptRuntime::Resource) + # @since 1.0.0 + module JavascriptRuntime + # A `javascript_runtime` resource to manage Javascript installations. + # + # @provides javascript_runtime + # @action install + # @action uninstall + # @example + # javascript_runtime '2.7' + class Resource < Chef::Resource + include Poise(inversion: true, container: true) + provides(:javascript_runtime) + actions(:install, :uninstall) + + # @!attribute version + # Version of Javascript to install. This is generally a NodeJS version + # but because of io.js there are shenanigans. + # @return [String] + # @example Install any version + # javascript_runtime 'any' do + # version '' + # end + attribute(:version, kind_of: String, name_attribute: true) + + # The path to the `node` binary for this Javascript installation. This is + # an output property. + # + # @return [String] + # @example + # execute "#{resources('javascript_runtime[nodejs]').javascript_binary} myapp.js" + def javascript_binary + provider_for_action(:javascript_binary).javascript_binary + end + + # The environment variables for this Javascript installation. This is an + # output property. + # + # @return [Hash] + # @example + # execute '/opt/myapp.js' do + # environment resources('javascript_runtime[nodejs]').javascript_environment + # end + def javascript_environment + provider_for_action(:javascript_environment).javascript_environment + end + + # The path to the `npm` binary for this Javascript installation. This is + # an output property. Can raise an exception if NPM is not supported for + # this runtime. + # + # @return [String] + # @example + # execute "#{resources('javascript_runtime[nodejs]').npm_binary} install" + def npm_binary + provider_for_action(:npm_binary).npm_binary + end + end + + # Providers can be found under lib/poise_javascript/javascript_providers/ + end + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/javascript_runtime_test.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/javascript_runtime_test.rb new file mode 100644 index 0000000..aac4ecd --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/javascript_runtime_test.rb @@ -0,0 +1,226 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'chef/resource' +require 'poise' + + +module PoiseJavascript + module Resources + # (see JavascriptRuntimeTest::Resource) + # @since 1.0.0 + # @api private + module JavascriptRuntimeTest + # A `javascript_runtime_test` resource for integration testing of this + # cookbook. This is an internal API and can change at any time. + # + # @provides javascript_runtime_test + # @action run + class Resource < Chef::Resource + include Poise + provides(:javascript_runtime_test) + actions(:run) + + attribute(:version, kind_of: String, name_attribute: true) + attribute(:runtime_provider, kind_of: Symbol) + attribute(:path, kind_of: String, default: lazy { default_path }) + attribute(:test_yo, equal_to: [true, false], default: true) + + def default_path + ::File.join('', 'root', "javascript_test_#{name}") + end + end + + # The default provider for `javascript_runtime_test`. + # + # @see Resource + # @provides javascript_runtime_test + class Provider < Chef::Provider + include Poise + provides(:javascript_runtime_test) + + # The `run` action for the `javascript_runtime_test` resource. + # + # @return [void] + def action_run + notifying_block do + # Top level directory for this test. + directory new_resource.path + + # Install and log the version. + javascript_runtime new_resource.name do + provider new_resource.runtime_provider if new_resource.runtime_provider + version new_resource.version + end + test_version + + # Create a package and test npm_install. + pkg_path = ::File.join(new_resource.path, 'pkg') + directory pkg_path + file ::File.join(pkg_path, 'package.json') do + content <<-EOH +{ + "name": "mypkg", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \\"Error: no test specified\\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "express": "4.13.3" + }, + "devDependencies": { + "handlebars": "4.0.2" + } +} +EOH + end + npm_install pkg_path do + notifies :create, sentinel_file('npm_install_one'), :immediately + end + npm_install pkg_path+'2' do + path pkg_path + notifies :create, sentinel_file('npm_install_two'), :immediately + end + test_require('express', pkg_path) + test_require('handlebars', pkg_path) + + # Test node_package. + test1_path = ::File.join(new_resource.path, 'test1') + directory test1_path + node_package 'express' do + path test1_path + notifies :create, sentinel_file('test1_express_one'), :immediately + end + node_package 'express two' do + package_name 'express' + path test1_path + notifies :create, sentinel_file('test1_express_two'), :immediately + end + node_package %w{gulp less} do + path test1_path + notifies :create, sentinel_file('test1_multi'), :immediately + end + node_package %w{express bower} do + path test1_path + notifies :create, sentinel_file('test1_multi_overlap'), :immediately + end + node_package 'bower' do + path test1_path + notifies :create, sentinel_file('test1_bower'), :immediately + end + node_package 'yo' do + path test1_path + version '1.4.5' + end if new_resource.test_yo + node_package 'forever' do + path test1_path + version '0.13.0' + end + test_require('express', test1_path, 'node_package_express') + test_require('gulp', test1_path) + test_require('less', test1_path) + test_require('bower', test1_path) + if new_resource.test_yo + test_require('yo', test1_path) + else + file ::File.join(new_resource.path, 'no_yo') + end + test_require('forever', test1_path) + + # Check we don't get cross talk between paths. + test2_path = ::File.join(new_resource.path, 'test2') + directory test2_path + node_package 'express' do + path test2_path + notifies :create, sentinel_file('test2_express'), :immediately + end + + # Test global installs. + node_package 'grunt-cli' do + notifies :create, sentinel_file('grunt_one'), :immediately + end + node_package 'grunt-cli two' do + package_name 'grunt-cli' + notifies :create, sentinel_file('grunt_two'), :immediately + end + test_require('grunt-cli', new_resource.path) + javascript_execute 'grunt-cli --version' do + command lazy { + # Check local/bin first and then just bin/. + grunt_path = ::File.expand_path('../../local/bin/grunt', javascript) + grunt_path = ::File.expand_path('../grunt', javascript) unless ::File.exist?(grunt_path) + "#{grunt_path} --version > #{::File.join(new_resource.path, 'grunt_version')}" + } + end + + end + end + + def sentinel_file(name) + file ::File.join(new_resource.path, "sentinel_#{name}") do + action :nothing + end + end + + private + + def test_version(javascript: new_resource.name) + # Only queue up this resource once, the ivar is just for tracking. + @javascript_version_test ||= file ::File.join(new_resource.path, 'javascript_version.js') do + user 'root' + group 'root' + mode '644' + content <<-EOH +var fs = require('fs'); +fs.writeFileSync(process.argv[2], process.version); +EOH + end + + javascript_execute "#{@javascript_version_test.path} #{::File.join(new_resource.path, 'version')}" do + javascript javascript if javascript + end + end + + def test_require(name, cwd, path=name, javascript: new_resource.name) + javascript_require_test = file ::File.join(cwd, 'javascript_require.js') do + user 'root' + group 'root' + mode '644' + content <<-EOH +var fs = require('fs'); +try { + var version = require(process.argv[2] + '/package.json').version; + fs.writeFileSync(process.argv[3], version); +} catch(e) { +} +EOH + end + + javascript_execute "#{javascript_require_test.path} #{name} #{::File.join(new_resource.path, "require_#{path}")}" do + javascript javascript if javascript + cwd cwd + end + end + + end + end + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/node_package.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/node_package.rb new file mode 100644 index 0000000..ecc75f8 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/node_package.rb @@ -0,0 +1,254 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/json_compat' +require 'chef/provider/package' +require 'chef/resource/package' +require 'poise' + +require 'poise_javascript/error' +require 'poise_javascript/javascript_command_mixin' + + +module PoiseJavascript + module Resources + # (see NodePackage::Resource) + # @since 1.0.0 + module NodePackage + # A `node_package` resource to manage Node.js packages using npm. + # + # @provides node_package + # @action install + # @action upgrade + # @action uninstall + # @example + # node_package 'express' do + # javascript '0.10' + # version '1.8.3' + # end + class Resource < Chef::Resource::Package + include PoiseJavascript::JavascriptCommandMixin + provides(:node_package) + # Manually create matchers because #actions is unreliable. + %i{install upgrade remove}.each do |action| + Poise::Helpers::ChefspecMatchers.create_matcher(:node_package, action) + end + + # @!attribute group + # System group to install the package. + # @return [String, Integer, nil] + attribute(:group, kind_of: [String, Integer, NilClass]) + # @!attribute path + # Path to install the package in to. If unset install using --global. + # @return [String, nil, false] + attribute(:path, kind_of: [String, NilClass, FalseClass]) + # @!attribute unsafe_perm + # Enable --unsafe-perm. + # @return [Boolean, nil] + attribute(:unsafe_perm, equal_to: [true, false, nil], default: true) + # @!attribute user + # System user to install the package. + # @return [String, Integer, nil] + attribute(:user, kind_of: [String, Integer, NilClass]) + + def initialize(*args) + super + # For older Chef. + @resource_name = :node_package + # We don't have these actions. + @allowed_actions.delete(:purge) + @allowed_actions.delete(:reconfig) + end + + # Upstream attribute we don't support. Sets are an error and gets always + # return nil. + # + # @api private + # @param arg [Object] Ignored + # @return [nil] + def response_file(arg=nil) + raise NoMethodError if arg + end + + # (see #response_file) + def response_file_variables(arg=nil) + raise NoMethodError if arg && arg != {} + end + end + + # The default provider for the `node_package` resource. + # + # @see Resource + class Provider < Chef::Provider::Package + include PoiseJavascript::JavascriptCommandMixin + provides(:node_package) + + # Load current and candidate versions for all needed packages. + # + # @api private + # @return [Chef::Resource] + def load_current_resource + @current_resource = new_resource.class.new(new_resource.name, run_context) + current_resource.package_name(new_resource.package_name) + check_package_versions(current_resource) + current_resource + end + + # Populate current and candidate versions for all needed packages. + # + # @api private + # @param resource [PoiseJavascript::Resources::NodePackage::Resource] + # Resource to load for. + # @return [void] + def check_package_versions(resource) + version_data = Hash.new {|hash, key| hash[key] = {current: nil, candidate: nil} } + # Get the version for everything currently installed. + list_args = npm_version?('>= 1.4.16') ? %w{--depth 0} : [] + npm_shell_out!('list', list_args).fetch('dependencies', {}).each do |pkg_name, pkg_data| + version_data[pkg_name][:current] = pkg_data['version'] + end + # If any requested packages are currently installed, run npm outdated + # to look for candidate versions. Older npm doesn't support --json + # here so you get slow behavior, sorry. + requested_packages = Set.new(Array(resource.package_name)) + if npm_version?('>= 1.3.16') && version_data.any? {|pkg_name, _pkg_vers| requested_packages.include?(pkg_name) } + outdated = npm_shell_out!('outdated') || {} + version_data.each do |pkg_name, pkg_vers| + pkg_vers[:candidate] = if outdated.include?(pkg_name) + outdated[pkg_name]['wanted'] + else + # If it was already installed and not listed in outdated, it + # must have been up to date already. + pkg_vers[:current] + end + end + end + # Check for candidates for anything else we didn't get from outdated. + requested_packages.each do |pkg_name| + version_data[pkg_name][:candidate] ||= npm_shell_out!('show', [pkg_name])['version'] + end + # Populate the current resource and candidate versions. Youch this is + # a gross mix of data flow. + if(resource.package_name.is_a?(Array)) + @candidate_version = [] + versions = [] + [resource.package_name].flatten.each do |name| + ver = version_data[name.downcase] + versions << ver[:current] + @candidate_version << ver[:candidate] + end + resource.version(versions) + else + ver = version_data[resource.package_name.downcase] + resource.version(ver[:current]) + @candidate_version = ver[:candidate] + end + end + + # Install package(s) using npm. + # + # @param name [String, Array] Name(s) of package(s). + # @param version [String, Array] Version(s) of package(s). + # @return [void] + def install_package(name, version) + args = [] + # Set --unsafe-perm unless the property is nil. + unless new_resource.unsafe_perm.nil? + args << '--unsafe-perm' + args << new_resource.unsafe_perm.to_s + end + # Build up the actual package install args. + if new_resource.source + args << new_resource.source + else + Array(name).zip(Array(version)) do |pkg_name, pkg_ver| + args << "#{pkg_name}@#{pkg_ver}" + end + end + npm_shell_out!('install', args, parse_json: false) + end + + # Upgrade and install are the same for NPM. + alias_method :upgrade_package, :install_package + + # Uninstall package(s) using npm. + # + # @param name [String, Array] Name(s) of package(s). + # @param version [String, Array] Version(s) of package(s). + # @return [void] + def remove_package(name, version) + npm_shell_out!('uninstall', [name].flatten, parse_json: false) + end + + private + + # Run an npm command. + # + # @param subcmd [String] Subcommand to run. + # @param args [Array] Command arguments. + # @param parse_json [Boolean] Parse the JSON on stdout. + # @return [Hash] + def npm_shell_out!(subcmd, args=[], parse_json: true) + cmd = [new_resource.npm_binary, subcmd, '--json'] + # If path is nil, we are in global mode. + cmd << '--global' unless new_resource.path + # Add the rest. + cmd.concat(args) + # If we are in global mode, cwd will be nil so probably just fine. Add + # the directory for the node binary to $PATH for post-install stuffs. + new_path = [::File.dirname(new_resource.javascript), ENV['PATH'].to_s].join(::File::PATH_SEPARATOR) + out = javascript_shell_out!(cmd, cwd: new_resource.path, group: new_resource.group, user: new_resource.user, environment: {'PATH' => new_path}) + if parse_json + # Parse the JSON. + if out.stdout.strip.empty? + {} + else + Chef::JSONCompat.parse(out.stdout) + end + else + out + end + end + + # Find the version of the current npm binary. + # + # @return [Gem::Version] + def npm_version + @npm_version ||= begin + out = javascript_shell_out!([new_resource.npm_binary, 'version']) + # Older NPM doesn't support --json here we get to regex! + # The line we want looks like: + # npm: '2.12.1' + if out.stdout =~ /npm: '([^']+)'/ + Gem::Version.new($1) + else + raise PoiseJavascript::Error.new("Unable to parse NPM version from #{out.stdout.inspect}") + end + end + end + + # Check the NPM version against a requirement. + # + # @param req [String] Requirement string in Gem::Requirement format. + # @return [Boolean] + def npm_version?(req) + Gem::Requirement.new(req).satisfied_by?(npm_version) + end + + end + end + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/npm_install.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/npm_install.rb new file mode 100644 index 0000000..df8af23 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/resources/npm_install.rb @@ -0,0 +1,98 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'chef/resource' +require 'poise' + +require 'poise_javascript/javascript_command_mixin' + + +module PoiseJavascript + module Resources + # (see NpmInstall::Resource) + # @since 1.0.0 + module NpmInstall + # A `npm_install` resource to install NPM packages based on a package.json. + # + # @provides npm_install + # @action install + # @example + # npm_install '/opt/myapp' + class Resource < Chef::Resource + include PoiseJavascript::JavascriptCommandMixin + provides(:npm_install) + actions(:install) + + # @!attribute path + # Directory to run `npm install` from. + # @return [String] + attribute(:path, kind_of: String, name_attribute: true) + # @!attribute group + # System group to install the packages. + # @return [String, Integer, nil] + attribute(:group, kind_of: [String, Integer, NilClass]) + # @!attribute production + # Enable production install mode. + # @return [Boolean] + attribute(:production, equal_to: [true, false], default: true) + # @!attribute timeout + # Command execution timeout. + # @return [Integer] + attribute(:timeout, kind_of: Integer, default: 900) + # @!attribute unsafe_perm + # Enable --unsafe-perm. + # @return [Boolean, nil] + attribute(:unsafe_perm, equal_to: [true, false, nil], default: true) + # @!attribute user + # System user to install the packages. + # @return [String, Integer, nil] + attribute(:user, kind_of: [String, Integer, NilClass]) + end + + # The default provider for `npm_install`. + # + # @see Resource + # @provides npm_install + class Provider < Chef::Provider + include Poise + include PoiseJavascript::JavascriptCommandMixin + provides(:npm_install) + + # The `install` action for the `npm_install` resource. + # + # @return [void] + def action_install + cmd = [new_resource.npm_binary, 'install'] + cmd << '--production' if new_resource.production + # Set --unsafe-perm unless the property is nil. + unless new_resource.unsafe_perm.nil? + cmd << '--unsafe-perm' + cmd << new_resource.unsafe_perm.to_s + end + # Add the directory for the node binary to $PATH for post-install stuffs. + new_path = [::File.dirname(new_resource.javascript), ENV['PATH'].to_s].join(::File::PATH_SEPARATOR) + output = javascript_shell_out!(cmd, cwd: new_resource.path, user: new_resource.user, group: new_resource.group, environment: {'PATH' => new_path}, timeout: new_resource.timeout).stdout + unless output.strip.empty? + # Any output means it did something. + new_resource.updated_by_last_action(true) + end + end + + end + end + end +end diff --git a/cookbooks/poise-javascript/files/halite_gem/poise_javascript/version.rb b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/version.rb new file mode 100644 index 0000000..f882cf4 --- /dev/null +++ b/cookbooks/poise-javascript/files/halite_gem/poise_javascript/version.rb @@ -0,0 +1,20 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseJavascript + VERSION = '1.1.0' +end diff --git a/cookbooks/poise-javascript/libraries/default.rb b/cookbooks/poise-javascript/libraries/default.rb new file mode 100644 index 0000000..6286990 --- /dev/null +++ b/cookbooks/poise-javascript/libraries/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) +require "poise_javascript/cheftie" diff --git a/cookbooks/poise-javascript/metadata.json b/cookbooks/poise-javascript/metadata.json new file mode 100644 index 0000000..6c5c746 --- /dev/null +++ b/cookbooks/poise-javascript/metadata.json @@ -0,0 +1 @@ +{"name":"poise-javascript","version":"1.1.0","description":"A Chef cookbook for managing Node.js and io.js installations.","long_description":"# Poise-Javascript Cookbook\n\n[![Build Status](https://img.shields.io/travis/poise/poise-javascript.svg)](https://travis-ci.org/poise/poise-javascript)\n[![Gem Version](https://img.shields.io/gem/v/poise-javascript.svg)](https://rubygems.org/gems/poise-javascript)\n[![Cookbook Version](https://img.shields.io/cookbook/v/poise-javascript.svg)](https://supermarket.chef.io/cookbooks/poise-javascript)\n[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-javascript.svg)](https://codecov.io/github/poise/poise-javascript)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-javascript.svg)](https://gemnasium.com/poise/poise-javascript)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nA [Chef](https://www.chef.io/) cookbook to provide a unified interface for\ninstalling server-side JavaScript runtimes like Node.js and io.js.\n\n## Quick Start\n\nTo install the latest available version of Node.js 0.12:\n\n```ruby\njavascript_runtime '0.12'\n```\n\n## Supported JavaScript Runtimes\n\nThis cookbook can install Node.js and io.js on Linux and OS X.\n\n## Requirements\n\nChef 12.1 or newer is required.\n\n## Attributes\n\nAttributes are used to configure the default recipe.\n\n* `node['poise-javascript']['install_nodejs']` – Install a Node.js runtime. *(default: true)*\n* `node['poise-javascript']['install_iojs']` – Install an io.js runtime. *(default: false)*\n\n## Recipes\n\n### `default`\n\nThe default recipe installs Node.js or io.js based on the node attributes. It is\nentirely optional and can be ignored in favor of direct use of the\n`javascript_runtime` resource.\n\n## Resources\n\n### `javascript_runtime`\n\nThe `javascript_runtime` resource installs a JavaScript interpreter.\n\n```ruby\njavascript_runtime '0.12'\n```\n\n#### Actions\n\n* `:install` – Install the JavaScript interpreter. *(default)*\n* `:uninstall` – Uninstall the JavaScript interpreter.\n\n#### Properties\n\n* `version` – Version of the runtime to install. If a partial version is given,\n use the latest available version matching that prefix. *(name property)*\n\n#### Provider Options\n\nThe `poise-javascript` library offers an additional way to pass configuration\ninformation to the final provider called \"options\". Options are key/value pairs\nthat are passed down to the `javascript_runtime` provider and can be used to control how it\ninstalls JavaScript. These can be set in the `javascript_runtime`\nresource using the `options` method, in node attributes or via the\n`javascript_runtime_options` resource. The options from all sources are merged\ntogether in to a single hash.\n\nWhen setting options in the resource you can either set them for all providers:\n\n```ruby\njavascript_runtime 'myapp' do\n version '0.10'\n options dev_package: false\nend\n```\n\nor for a single provider:\n\n```ruby\njavascript_runtime 'myapp' do\n version '0.10'\n options :system, dev_package: false\nend\n```\n\nSetting via node attributes is generally how an end-user or application cookbook\nwill set options to customize installations in the library cookbooks they are using.\nYou can set options for all installations or for a single runtime:\n\n```ruby\n# Global, for all installations.\noverride['poise-javascript']['options']['version'] = '0.10'\n# Single installation.\noverride['poise-javascript']['myapp']['version'] = 'iojs'\n```\n\nThe `javascript_runtime_options` resource is also available to set node attributes\nfor a specific installation in a DSL-friendly way:\n\n```ruby\njavascript_runtime_options 'myapp' do\n version 'iojs'\nend\n```\n\nUnlike resource attributes, provider options can be different for each provider.\nNot all providers support the same options so make sure to the check the\ndocumentation for each provider to see what options the use.\n\n### `javascript_runtime_options`\n\nThe `javascript_runtime_options` resource allows setting provider options in a\nDSL-friendly way. See [the Provider Options](#provider-options) section for more\ninformation about provider options overall.\n\n```ruby\njavascript_runtime_options 'myapp' do\n version 'iojs'\nend\n```\n\n#### Actions\n\n* `:run` – Apply the provider options. *(default)*\n\n#### Properties\n\n* `resource` – Name of the `javascript_runtime` resource. *(name property)*\n* `for_provider` – Provider to set options for.\n\nAll other attribute keys will be used as options data.\n\n### `javascript_execute`\n\nThe `javascript_execute` resource executes a JavaScript script using the configured runtime.\n\n```ruby\njavascript_execute 'myapp.js' do\n user 'myuser'\nend\n```\n\nThis uses the built-in `execute` resource and supports all the same properties.\n\n#### Actions\n\n* `:run` – Execute the script. *(default)*\n\n#### Properties\n\n* `command` – Script and arguments to run. Must not include the `node`. *(name attribute)*\n* `javascript` – Name of the `javascript_runtime` resource to use. If not specified, the\n most recently declared `javascript_runtime` will be used. Can also be set to the\n full path to a `node` binary.\n\nFor other properties see the [Chef documentation](https://docs.chef.io/resource_execute.html#attributes).\n\n### `node_package`\n\nThe `node_package` resource installs Node.js packages using\n[NPM](https://www.npmjs.com/).\n\n```ruby\nnode_package 'express' do\n version '4.13.3'\nend\n```\n\nThis uses the built-in `package` resource and supports the same actions and\nproperties. Multi-package installs are supported using the standard syntax.\n\n#### Actions\n\n* `:install` – Install the package. *(default)*\n* `:upgrade` – Upgrade the package.\n* `:remove` – Uninstall the package.\n\nThe `:purge` and `:reconfigure` actions are not supported.\n\n#### Properties\n\n* `group` – System group to install the package.\n* `package_name` – Package or packages to install. *(name property)*\n* `path` – Path to install the package in to. If unset install using `--global`.\n *(default: nil)*\n* `version` – Version or versions to install.\n* `javascript` – Name of the `javascript_runtime` resource to use. If not specified, the\n most recently declared `javascript_runtime` will be used. Can also be set to the\n full path to a `node` binary.\n* `unsafe_perm` – Enable `--unsafe-perm`. *(default: true)*\n* `user` – System user to install the package.\n\nFor other properties see the [Chef documentation](https://docs.chef.io/resource_package.html#attributes).\nThe `response_file`, `response_file_variables`, and `source` properties are not\nsupported.\n\n### `npm_install`\n\nThe `npm_install` resource runs `npm install` for a package.\n\n```ruby\nnpm_install '/opt/myapp'\n```\n\nThe underlying `npm install` command will run on every converge, but notifications\nwill only be triggered if a package is actually installed.\n\n#### Actions\n\n* `:install` – Run `npm install`. *(default)*\n\n#### Properties\n\n* `path` – Path to the package folder containing a `package.json`. *(name attribute)*\n* `group` – System group to install the packages.\n* `javascript` – Name of the `javascript_runtime` resource to use. If not specified, the\n most recently declared `javascript_runtime` will be used. Can also be set to the\n full path to a `node` binary.\n* `production` – Enable production install mode. *(default: true)*\n* `unsafe_perm` – Enable `--unsafe-perm`. *(default: true)*\n* `user` – System user to install the packages.\n\n## Javascript Providers\n\n### Common Options\n\nThese provider options are supported by all providers.\n\n* `version` – Override the runtime version.\n\n### `system`\n\nThe `system` provider installs Node.js using system packages. This is currently\nonly tested on platforms using `apt-get` and `yum` (Debian, Ubuntu, RHEL, CentOS\nAmazon Linux, and Fedora). It may work on other platforms but is untested.\n\n```ruby\njavascript_runtime 'myapp' do\n provider :system\n version '0.10'\nend\n```\n\n#### Options\n\n* `dev_package` – Install the package with the headers and other development\n files. Can be set to a string to select the dev package specifically.\n *(default: true)*\n* `package_name` – Override auto-detection of the package name.\n* `package_upgrade` – Install using action `:upgrade`. *(default: false)*\n* `package_version` – Override auto-detection of the package version.\n\n### `scl`\n\nThe `scl` provider installs Node.js using the [Software Collections](https://www.softwarecollections.org/)\npackages. This is only available on RHEL and CentOS. SCL offers more\nrecent versions of Node.js than the system packages for the most part. If an SCL\npackage exists for the requests version, it will be used in preference to the\n`system` provider.\n\n```ruby\njavascript_runtime 'myapp' do\n provider :scl\n version '0.10'\nend\n```\n\n### `nodejs`\n\nThe `nodejs` provider installs Node.js from the static binaries on nodejs.org.\nSupport is included for Linux and OS X.\n\n```ruby\njavascript_runtime 'myapp' do\n provider :nodejs\n version '0.12'\nend\n```\n\n#### Options\n\n* `path` – Folder to install Node.js to. *(default: /opt/nodejs-)*\n* `static_version` – Specific version number to use for computing the URL and\n path. *(default: automatic from `version`)*\n* `strip_components` – Value to pass to tar --strip-components. *(automatic)*\n* `url` – URL template to download the archive from. *(default: automatic)*\n\n### `iojs`\n\nThe `iojs` provider installs io.js from the static binaries on iojs.org.\nSupport is included for Linux and OS X.\n\n```ruby\njavascript_runtime 'myapp' do\n provider :iojs\n version '3'\nend\n```\n\n#### Options\n\n* `path` – Folder to install io.js to. *(default: /opt/iojs-)*\n* `static_version` – Specific version number to use for computing the URL and\n path. *(default: automatic from `version`)*\n* `strip_components` – Value to pass to tar --strip-components. *(automatic)*\n* `url` – URL template to download the archive from. *(default: automatic)*\n\n## Sponsors\n\nThe Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/).\n\n## License\n\nCopyright 2015-2016, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainer":"Noah Kantrowitz","maintainer_email":"noah@coderanger.net","license":"Apache 2.0","platforms":{},"dependencies":{"poise":"~> 2.0","poise-languages":"~> 2.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{},"source_url":"https://github.com/poise/poise-javascript","issues_url":"https://github.com/poise/poise-javascript/issues","chef_version":"~> 12","ohai_version":{}} \ No newline at end of file diff --git a/cookbooks/poise-javascript/recipes/default.rb b/cookbooks/poise-javascript/recipes/default.rb new file mode 100644 index 0000000..eb8b5c7 --- /dev/null +++ b/cookbooks/poise-javascript/recipes/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Default runtimes, last one will be the default. +javascript_runtime 'iojs' if node['poise-javascript']['install_iojs'] +javascript_runtime 'nodejs' if node['poise-javascript']['install_nodejs'] diff --git a/cookbooks/poise-languages/CHANGELOG.md b/cookbooks/poise-languages/CHANGELOG.md new file mode 100644 index 0000000..0a00a35 --- /dev/null +++ b/cookbooks/poise-languages/CHANGELOG.md @@ -0,0 +1,79 @@ +# Changelog + +## v2.1.0 + +* Allow customizing properties on the system package install resource via a block + +## v2.0.5 + +* Fixes to work with the latest Chef (again). + +## v2.0.4 + +* Fixes to work with the latest Chef. + +## v2.0.3 + +* Correct the subscription repository name used for SCLs on RedHat. + +## v2.0.2 + +* Don't try to use SCL providers on Amazon Linux. + +## v2.0.1 + +* Don't error on `Chef::Decorator::Lazy` proxy objects for `candidate_version`. +* Retry system and SCL package installs because transient network failures. + +## v2.0.0 + +* Backwards-incompatible change to SCL management to comply with their new repo + packages and layout. Uses `centos-release-scl-rh` repo package or the + `rhel-variant-rhscl` RedHat subscription. + +## v1.4.0 + +* Use `poise-archive` to unpack static binary archives. This should work better + on AIX and Solaris, as well as making it easier to add more archive formats in + the future. + +## v1.3.3 + +* [#3](https://github.com/poise/poise-languages/pull/3) Fix `static` binary + installation on AIX and Solaris. +* Only run the candidate version check for `system` installs when we aren't + passing in package_version. + +## v1.3.2 + +* Handle static archive unpacking correctly when a single download is shared + between two paths. + +## v1.3.1 + +* Fix system package installs on OS X. + +## v1.3.0 + +* `%{machine_label}` is available in URL template for static download. +* Automatically retry `remote_file` downloads to handle transient HTTP failures. +* All `*_shell_out` language command helpers use `poise_shell_out` to set `$HOME` + and other environment variables by default. + +## v1.2.0 + +* Support for installing development headers with SCL providers. +* Add `PoiseLanguages::Utils.shelljoin` for encoding command arrays with some + bash metadata characters allowed. +* [#1](https://github.com/poise/poise-languages/pull/1) Fix typo in gemspec. + +## v1.1.0 + +* Add helpers for installing from static archives. +* Improve auto-selection rules for system and SCL providers. +* Support SCL packages that depend on other SCL packages. +* Support Ruby 2.0 again. + +## v1.0.0 + +* Initial release! diff --git a/cookbooks/poise-languages/README.md b/cookbooks/poise-languages/README.md new file mode 100644 index 0000000..30bc6da --- /dev/null +++ b/cookbooks/poise-languages/README.md @@ -0,0 +1,27 @@ +# Poise-Languages Cookbook + +[![Build Status](https://img.shields.io/travis/poise/poise-languages.svg)](https://travis-ci.org/poise/poise-languages) +[![Gem Version](https://img.shields.io/gem/v/poise-languages.svg)](https://rubygems.org/gems/poise-languages) +[![Cookbook Version](https://img.shields.io/cookbook/v/poise-languages.svg)](https://supermarket.chef.io/cookbooks/poise-languages) +[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-languages.svg)](https://codecov.io/github/poise/poise-languages) +[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-languages.svg)](https://gemnasium.com/poise/poise-languages) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +Shared support code for Poise's language cookbooks like poise-ruby and +poise-python. + +## License + +Copyright 2015-2017, Noah Kantrowitz + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages.rb new file mode 100644 index 0000000..9952907 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages.rb @@ -0,0 +1,26 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseLanguages + autoload :Command, 'poise_languages/command' + autoload :Error, 'poise_languages/error' + autoload :Scl, 'poise_languages/scl' + autoload :Static, 'poise_languages/static' + autoload :System, 'poise_languages/system' + autoload :Utils, 'poise_languages/utils' + autoload :VERSION, 'poise_languages/version' +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/command.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/command.rb new file mode 100644 index 0000000..bd46621 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/command.rb @@ -0,0 +1,25 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseLanguages + # A namespace for language-command-related stuff. + # + # @since 1.0.0 + module Command + autoload :Mixin, 'poise_languages/command/mixin' + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/command/mixin.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/command/mixin.rb new file mode 100644 index 0000000..15e2c7d --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/command/mixin.rb @@ -0,0 +1,241 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'shellwords' + +require 'poise' + +require 'poise_languages/error' +require 'poise_languages/utils' + + +module PoiseLanguages + module Command + # A mixin for resources and providers that run language commands. + # + # @since 1.0.0 + module Mixin + include Poise::Utils::ResourceProviderMixin + + # A mixin for resources that run language commands. Also available as a + # parameterized mixin via `include PoiseLanguages::Command::Mixin::Resource(name)`. + # + # @example + # class MyLangThing + # include PoiseLanguages::Command::Mixin::Resource(:mylang) + # # ... + # end + module Resource + include Poise::Resource + poise_subresource(true) + + private + + # Implementation of the $name accessor. + # + # @api private + # @param name [Symbol] Language name. + # @param runtime [Symbol] Language runtime resource name. + # @param val [String, Chef::Resource, Poise::NOT_PASSED, nil] Accessor value. + # @return [String] + def language_command_runtime(name, runtime, default_binary, val=Poise::NOT_PASSED) + unless val == Poise::NOT_PASSED + path_arg = parent_arg = nil + # Figure out which property we are setting. + if val.is_a?(String) + # Check if it is a runtime resource. + begin + parent_arg = run_context.resource_collection.find("#{runtime}[#{val}]") + rescue Chef::Exceptions::ResourceNotFound + # Check if something looks like a path, defined as containing + # either / or \. While a single word could be a path, I think the + # UX win of better error messages should take priority. + might_be_path = val =~ %r{/|\\} + if might_be_path + Chef::Log.debug("[#{self}] #{runtime}[#{val}] not found, treating it as a path") + path_arg = val + else + # Surface the error up to the user. + raise + end + end + else + parent_arg = val + end + # Set both attributes. + send(:"parent_#{name}", parent_arg) + set_or_return(name, path_arg, kind_of: [String, NilClass]) + else + # Getter behavior. Using the ivar directly is kind of gross but oh well. + instance_variable_get(:"@#{name}") || default_language_command_runtime(name, default_binary) + end + end + + # Compute the path to the default runtime binary. + # + # @api private + # @param name [Symbol] Language name. + # @return [String] + def default_language_command_runtime(name, default_binary) + parent = send(:"parent_#{name}") + if parent + parent.send(:"#{name}_binary") + else + PoiseLanguages::Utils.which(default_binary || name.to_s) + end + end + + # Inherit language parent from another resource. + # + # @api private + # @param name [Symbol] Language name. + # @param resource [Chef::Resource] Resource to inherit from. + # @return [void] + def language_command_runtime_from_parent(name, resource) + parent = resource.send(:"parent_#{name}") + if parent + send(:"parent_#{name}", parent) + else + path = resource.send(name) + if path + send(name, path) + end + end + end + + module ClassMethods + # Configure this module or class for a specific language. + # + # @param name [Symbol] Language name. + # @param runtime [Symbol] Language runtime resource name. + # @param timeout [Boolean] Enable the timeout attribute. + # @param default_binary [String] Name of the default language binary. + # @return [void] + def language_command_mixin(name, runtime: :"#{name}_runtime", timeout: true, default_binary: nil) + # Create the parent attribute. + parent_attribute(name, type: runtime, optional: true) + + # Timeout attribute for the shell_out wrappers in the provider. + attribute(:timeout, kind_of: Integer, default: 900) if timeout + + # Create the main accessor for the parent/path. + define_method(name) do |val=Poise::NOT_PASSED| + language_command_runtime(name, runtime, default_binary, val) + end + + # Create the method to inherit settings from another resource. + define_method(:"#{name}_from_parent") do |resource| + language_command_runtime_from_parent(name, resource) + end + private :"#{name}_from_parent" + end + + def language_command_default_binary(val=Poise::NOT_PASSED) + @language_command_default_binary = val if val != Poise::NOT_PASSED + @language_command_default_binary + end + + # @api private + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + Poise::Utils.parameterized_module(self) {|*args| language_command_mixin(*args) } + end # /module Resource + + # A mixin for providers that run language commands. + module Provider + include Poise::Utils::ShellOut + + private + + # Run a command using the configured language via `shell_out`. + # + # @api private + # @param name [Symbol] Language name. + # @param command_args [Array] Arguments to `shell_out`. + # @return [Mixlib::ShellOut] + def language_command_shell_out(name, *command_args, **options) + # Inject our environment variables if needed. + options[:environment] ||= {} + parent = new_resource.send(:"parent_#{name}") + if parent + options[:environment].update(parent.send(:"#{name}_environment")) + end + # Inject other options. + options[:timeout] ||= new_resource.timeout + # Find the actual binary to use. Raise an exception if we see false + # which happens if no parent resource is found, no explicit default + # binary was given, and which() fails to find a thing. + binary = new_resource.send(name) + raise Error.new("Unable to find a #{name} binary for command: #{command_args.is_a?(Array) ? Shellwords.shelljoin(command_args) : command_args}") unless binary + command = if command_args.length == 1 && command_args.first.is_a?(String) + # String mode, sigh. + "#{Shellwords.escape(binary)} #{command_args.first}" + else + # Array mode. Handle both ('one', 'two') and (['one', 'two']). + [binary] + command_args.flatten + end + Chef::Log.debug("[#{new_resource}] Running #{name} command: #{command.is_a?(Array) ? Shellwords.shelljoin(command) : command}") + # Run the command + poise_shell_out(command, options) + end + + # Run a command using the configured language via `shell_out!`. + # + # @api private + # @param name [Symbol] Language name. + # @param command_args [Array] Arguments to `shell_out!`. + # @return [Mixlib::ShellOut] + def language_command_shell_out!(name, *command_args) + send(:"#{name}_shell_out", *command_args).tap(&:error!) + end + + module ClassMethods + # Configure this module or class for a specific language. + # + # @param name [Symbol] Language name. + # @return [void] + def language_command_mixin(name) + define_method(:"#{name}_shell_out") do |*command_args| + language_command_shell_out(name, *command_args) + end + private :"#{name}_shell_out" + + define_method(:"#{name}_shell_out!") do |*command_args| + language_command_shell_out!(name, *command_args) + end + private :"#{name}_shell_out!" + end + + # @api private + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + Poise::Utils.parameterized_module(self) {|*args| language_command_mixin(*args) } + end # /module Provider + + Poise::Utils.parameterized_module(self) {|*args| language_command_mixin(*args) } + end # /module Mixin + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/error.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/error.rb new file mode 100644 index 0000000..ddfadee --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/error.rb @@ -0,0 +1,21 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseLanguages + class Error < ::Exception + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/scl.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/scl.rb new file mode 100644 index 0000000..b0b5771 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/scl.rb @@ -0,0 +1,24 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseLanguages + module Scl + autoload :Mixin, 'poise_languages/scl/mixin' + autoload :Resource, 'poise_languages/scl/resource' + autoload :Provider, 'poise_languages/scl/resource' + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/scl/mixin.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/scl/mixin.rb new file mode 100644 index 0000000..63f6cd0 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/scl/mixin.rb @@ -0,0 +1,134 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_languages/scl/resource' + + +module PoiseLanguages + module Scl + module Mixin + private + + def install_scl_package + pkg = scl_package + poise_languages_scl options[:package_name] || pkg[:name] do + action :upgrade if options[:package_upgrade] + dev_package options[:dev_package] == true ? pkg[:devel_name] : options[:dev_package] + parent new_resource + version options[:package_version] + end + end + + def uninstall_scl_package + install_scl_package.tap do |r| + r.action(:uninstall) + end + end + + def scl_package + @scl_package ||= self.class.find_scl_package(node, options['version']).tap do |p| + raise PoiseLanguages::Error.new("No SCL repoistory package for #{node['platform']} #{node['platform_version']}") unless p + end + end + + def scl_folder + ::File.join('', 'opt', 'rh', scl_package[:name]) + end + + def scl_environment + parse_enable_file(::File.join(scl_folder, 'enable')) + end + + # Parse an SCL enable file to extract the environment variables set in it. + # + # @param path [String] Path to the enable file. + # @return [Hash] + def parse_enable_file(path, env={}) + # Doesn't exist yet, so running Python will fail anyway. Just make sure + # it fails in the expected way. + return {} unless File.exist?(path) + # Yes, this is a bash parser in regex. Feel free to be mad at me. + IO.readlines(path).inject(env) do |memo, line| + if match = line.match(/^export (\w+)=(.*)$/) + memo[match[1]] = match[2].gsub(/\$(?:\{(\w+)(:\+:\$\{\w+\})?\}|(\w+))/) do + key = $1 || $3 + value = (memo[key] || ENV[key]).to_s + value = ":#{value}" if $2 && !value.empty? + value + end + elsif match = line.match(/^\. scl_source enable (\w+)$/) + # Parse another file. + memo.update(parse_enable_file(::File.join('', 'opt', 'rh', match[1], 'enable'), memo)) + end + memo + end + end + + module ClassMethods + def provides_auto?(node, resource) + # They don't build 32-bit versions for these and only for RHEL/CentOS. + # TODO: What do I do about Fedora and/or Amazon? + return false unless node['kernel']['machine'] == 'x86_64' && node.platform?('redhat', 'centos') + version = inversion_options(node, resource)['version'] + !!find_scl_package(node, version) + end + + # Set some default inversion provider options. Package name can't get + # a default value here because that would complicate the handling of + # {system_package_candidates}. + # + # @api private + def default_inversion_options(node, resource) + super.merge({ + # Install dev headers? + dev_package: true, + # Manual overrides for package name and/or version. + package_name: nil, + package_version: nil, + # Set to true to use action :upgrade on system packages. + package_upgrade: false, + }) + end + + def find_scl_package(node, version) + platform_version = ::Gem::Version.create(node['platform_version']) + # Filter out anything that doesn't match this EL version. + candidate_packages = scl_packages.select {|p| p[:platform_version].satisfied_by?(platform_version) } + # Find something with a prefix match on the Python version. + candidate_packages.find {|p| p[:version].start_with?(version) } + end + + private + + def scl_packages + @scl_packages ||= [] + end + + def scl_package(version, name, devel_name=nil, platform_version='>= 6.0') + scl_packages << {version: version, name: name, devel_name: devel_name, platform_version: ::Gem::Requirement.create(platform_version)} + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + + end + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/scl/resource.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/scl/resource.rb new file mode 100644 index 0000000..1e92c2e --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/scl/resource.rb @@ -0,0 +1,159 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'chef/provider' +require 'poise' + + +module PoiseLanguages + module Scl + # A `poise_language_scl` resource to manage installing a language from + # SCL packages. This is an internal implementation detail of + # poise-languages. + # + # @api private + # @since 1.0 + # @provides poise_languages_scl + # @action install + # @action uninstall + class Resource < Chef::Resource + include Poise + provides(:poise_languages_scl) + actions(:install, :upgrade, :uninstall) + + # @!attribute package_name + # Name of the SCL package for the language. + # @return [String] + attribute(:package_name, kind_of: String, name_attribute: true) + # @!attribute dev_package + # Name of the -devel package with headers and whatnot. + # @return [String, nil] + attribute(:dev_package, kind_of: [String, NilClass]) + # @!attribute version + # Version of the SCL package(s) to install. If unset, follows the same + # semantics as the core `package` resource. + # @return [String, nil] + attribute(:version, kind_of: [String, NilClass]) + # @!attribute parent + # Resource for the language runtime. Used only for messages. + # @return [Chef::Resource] + attribute(:parent, kind_of: Chef::Resource, required: true) + end + + # The default provider for `poise_languages_scl`. + # + # @api private + # @since 1.0 + # @see Resource + # @provides poise_languages_scl + class Provider < Chef::Provider + include Poise + provides(:poise_languages_scl) + + # The `install` action for the `poise_languages_scl` resource. + # + # @return [void] + def action_install + notifying_block do + install_scl_repo + flush_yum_cache + install_scl_package(:install) + install_scl_devel_package(:install) if new_resource.dev_package + end + end + + # The `upgrade` action for the `poise_languages_scl` resource. + # + # @return [void] + def action_upgrade + notifying_block do + install_scl_repo + flush_yum_cache + install_scl_package(:upgrade) + install_scl_devel_package(:upgrade) if new_resource.dev_package + end + end + + # The `uninstall` action for the `poise_languages_scl` resource. + # + # @return [void] + def action_uninstall + notifying_block do + uninstall_scl_devel_package if new_resource.dev_package + uninstall_scl_package + end + end + + private + + def install_scl_repo + if node.platform?('redhat') + # Set up the real RHSCL subscription. + # NOTE: THIS IS NOT TESTED BECAUSE REDHAT DOESN'T OFFER ANY WAY TO DO + # AUTOMATED TESTING. IF YOU USE REDHAT AND THIS BREAKS, PLEASE LET ME + # KNOW BY FILING A GITHUB ISSUE AT http://github.com/poise/poise-languages/issues/new. + repo_name = "rhel-server-rhscl-#{node['platform_version'][0]}-rpms" + execute "subscription-manager repos --enable #{repo_name}" do + not_if { shell_out!('subscription-manager repos --list').stdout.include?(repo_name) } + end + else + package 'centos-release-scl-rh' do + # Using upgrade here because changes very very rare and always + # important when they happen. If this breaks your prod infra, I'm + # sorry :-( + action :upgrade + retries 5 + end + end + end + + def flush_yum_cache + ruby_block 'flush_yum_cache' do + block do + # Equivalent to flush_cache after: true + Chef::Provider::Package::Yum::YumCache.instance.reload + end + end + end + + def install_scl_package(action) + package new_resource.package_name do + action action + retries 5 + version new_resource.version + end + end + + def install_scl_devel_package(action) + package new_resource.dev_package do + action action + retries 5 + version new_resource.version + end + end + + def uninstall_scl_package + install_scl_package(:remove) + end + + def uninstall_scl_devel_package + install_scl_devel_package(:remove) + end + + end + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/static.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/static.rb new file mode 100644 index 0000000..efdbe10 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/static.rb @@ -0,0 +1,34 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/utils' + + +module PoiseLanguages + # Helpers for installing languages from static archives. + # + # @since 1.1.0 + module Static + autoload :Mixin, 'poise_languages/static/mixin' + autoload :Resource, 'poise_languages/static/resource' + autoload :Provider, 'poise_languages/static/resource' + + Poise::Utils.parameterized_module(self) do |opts| + require 'poise_languages/static/mixin' + include PoiseLanguages::Static::Mixin(opts) + end + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/static/mixin.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/static/mixin.rb new file mode 100644 index 0000000..d189331 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/static/mixin.rb @@ -0,0 +1,144 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_languages/static/resource' + + +module PoiseLanguages + module Static + # Mixin for language providers to install from static archives. + # + # @since 1.1.0 + module Mixin + private + + def install_static + url = static_url + poise_languages_static static_folder do + source url + strip_components options['strip_components'] + end + end + + def uninstall_static + install_static.tap do |r| + r.action(:uninstall) + end + end + + def static_folder + options['path'] || ::File.join('', 'opt', "#{self.class.static_name}-#{options['static_version']}") + end + + def static_url + options['url'] % static_url_variables + end + + def static_url_variables + { + version: options['static_version'], + kernel: node['kernel']['name'].downcase, + machine: node['kernel']['machine'], + machine_label: self.class.static_machine_label_wrapper(node, new_resource), + } + end + + module ClassMethods + attr_accessor :static_name + attr_accessor :static_versions + attr_accessor :static_machines + attr_accessor :static_url + attr_accessor :static_strip_components + attr_accessor :static_retries + + def provides_auto?(node, resource) + # Check that the version starts with our project name and the machine + # we are on is supported. + resource.version.to_s =~ /^#{static_name}(-|$)/ && static_machines.include?(static_machine_label_wrapper(node, resource)) + end + + # Set some default inversion provider options. Package name can't get + # a default value here because that would complicate the handling of + # {system_package_candidates}. + # + # @api private + def default_inversion_options(node, resource) + super.merge({ + # Path to install the package. Defaults to /opt/name-version. + path: nil, + # Number of times to retry failed downloads. + retries: static_retries, + # Full version number for use in interpolation. + static_version: static_version(node, resource), + # Value to pass to tar --strip-components. + strip_components: static_strip_components, + # URL template to download from. + url: static_url, + }) + end + + def static_options(name: nil, versions: [], machines: %w{linux-i686 linux-x86_64}, url: nil, strip_components: 1, retries: 5) + raise PoiseLanguages::Error.new("Static archive URL is required, on #{self}") unless url + self.static_name = name || provides.to_s + self.static_versions = versions + self.static_machines = Set.new(machines) + self.static_url = url + self.static_strip_components = strip_components + self.static_retries = retries + end + + def static_version(node, resource) + raw_version = resource.version.to_s.gsub(/^#{static_name}(-|$)/, '') + if static_versions.include?(raw_version) + raw_version + else + # Prefix match or just use the given version number if not found. + # This allow mild future proofing in some cases. + static_versions.find {|v| v.start_with?(raw_version) } || raw_version + end + end + + def static_machine_label(node, _resource=nil) + "#{node['kernel']['name'].downcase}-#{node['kernel']['machine']}" + end + + # Wrapper for {#static_machine_label} because I need to add an argument. + # This preserves backwards compat. + # + # @api private + def static_machine_label_wrapper(node, resource) + args = [node] + arity = method(:static_machine_label).arity + args << resource if arity > 1 || arity < 0 + static_machine_label(*args) + end + + def included(klass) + super + klass.extend ClassMethods + end + + end + + extend ClassMethods + + Poise::Utils.parameterized_module(self) do |opts| + static_options(opts) + end + + end + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/static/resource.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/static/resource.rb new file mode 100644 index 0000000..150eb44 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/static/resource.rb @@ -0,0 +1,139 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'chef/provider' +require 'poise' + + +module PoiseLanguages + module Static + # A `poise_languages_static` resource to manage installing a language from + # static binary archives. This is an internal implementation detail of + # poise-languages. + # + # @api private + # @since 1.1.0 + # @provides poise_languages_static + # @action install + # @action uninstall + class Resource < Chef::Resource + include Poise + provides(:poise_languages_static) + actions(:install, :uninstall) + + # @!attribute path + # Directory to install to. + # @return [String] + attribute(:path, kind_of: String, name_attribute: true) + # @!attribute download_retries + # Number of times to retry failed downloads. Defaults to 5. + # @return [Integer] + attribute(:download_retries, kind_of: Integer, default: 5) + # @!attribute source + # URL to download from. + # @return [String] + attribute(:source, kind_of: String, required: true) + # @!attribute strip_components + # Value to pass to tar --strip-components. + # @return [String, Integer, nil] + attribute(:strip_components, kind_of: [String, Integer, NilClass], default: 1) + + def cache_path + @cache_path ||= ::File.join(Chef::Config[:file_cache_path], source.split(/\//).last) + end + end + + # The default provider for `poise_languages_static`. + # + # @api private + # @since 1.0 + # @see Resource + # @provides poise_languages_static + class Provider < Chef::Provider + include Poise + provides(:poise_languages_static) + + # The `install` action for the `poise_languages_static` resource. + # + # @return [void] + def action_install + notifying_block do + download_archive + create_directory + # Unpack is handled as a notification from download_archive. + end + end + + # The `uninstall` action for the `poise_languages_static` resource. + # + # @return [void] + def action_uninstall + notifying_block do + delete_archive + delete_directory + end + end + + private + + def create_directory + unpack_resource = unpack_archive + directory new_resource.path do + user 0 + group 0 + mode '755' + notifies :unpack, unpack_resource, :immediately + end + end + + def download_archive + unpack_resource = unpack_archive + remote_file new_resource.cache_path do + source new_resource.source + owner 0 + group 0 + mode '644' + notifies :unpack, unpack_resource, :immediately if ::File.exist?(new_resource.path) + retries new_resource.download_retries + end + end + + def unpack_archive + @unpack_archive ||= poise_archive new_resource.cache_path do + # Run via notification from #download_archive and #create_directory. + action :nothing + destination new_resource.path + strip_components new_resource.strip_components + end + end + + def delete_archive + file new_resource.cache_path do + action :delete + end + end + + def delete_directory + directory new_resource.path do + action :delete + recursive true + end + end + + end + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/system.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/system.rb new file mode 100644 index 0000000..5ece43c --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/system.rb @@ -0,0 +1,24 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseLanguages + module System + autoload :Mixin, 'poise_languages/system/mixin' + autoload :Resource, 'poise_languages/system/resource' + autoload :Provider, 'poise_languages/system/resource' + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/system/mixin.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/system/mixin.rb new file mode 100644 index 0000000..8b1764e --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/system/mixin.rb @@ -0,0 +1,170 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_languages/system/resource' + + +module PoiseLanguages + module System + module Mixin + + private + + # Install a language using system packages. + # + # @api public + # @return [PoiseLanguages::System::Resource] + def install_system_packages(&block) + dev_package_overrides = system_dev_package_overrides + poise_languages_system system_package_name do + # Otherwise use the default install action. + action(:upgrade) if options['package_upgrade'] + parent new_resource + # Don't pass true because we want the default computed behavior for that. + dev_package options['dev_package'] unless options['dev_package'] == true + dev_package_overrides dev_package_overrides + package_version options['package_version'] if options['package_version'] + version options['version'] + instance_exec(&block) if block + end + end + + # Uninstall a language using system packages. + # + # @api public + # @return [PoiseLanguages::System::Resource] + def uninstall_system_packages(&block) + install_system_packages.tap do |r| + r.action(:uninstall) + r.instance_exec(&block) if block + end + end + + # Compute all possible package names for a given language version. Must be + # implemented by mixin users. Versions are expressed as prefixes so '' + # matches all versions, '2' matches 2.x. + # + # @abstract + # @api public + # @param version [String] Language version prefix. + # @return [Array] + def system_package_candidates(version) + raise NotImplementedError + end + + # Compute the default package name for the base package for this language. + # + # @api public + # @return [String] + def system_package_name + # If we have an override, just use that. + return options['package_name'] if options['package_name'] + # Look up all packages for this language on this platform. + system_packages = self.class.packages && node.value_for_platform(self.class.packages) + if !system_packages && self.class.default_package + Chef::Log.debug("[#{new_resource}] No known packages for #{node['platform']} #{node['platform_version']}, defaulting to '#{self.class.default_package}'.") if self.class.packages + system_packages = Array(self.class.default_package) + end + + # Find the first value on system_package_candidates that is in system_packages. + system_package_candidates(options['version'].to_s).each do |name| + return name if system_packages.include?(name) + end + # No valid candidate. Sad trombone. + raise PoiseLanguages::Error.new("Unable to find a candidate package for version #{options['version'].to_s.inspect}. Please set package_name provider option for #{new_resource}.") + end + + # A hash mapping package names to their override dev package name. + # + # @api public + # @return [Hash] + def system_dev_package_overrides + {} + end + + module ClassMethods + # Install this as a default provider if nothing else matched. Might not + # work, but worth a try at least for unknown platforms. Windows is a + # whole different story, and OS X might work sometimes so at least try. + # + # @api private + def provides_auto?(node, resource) + !node.platform_family?('windows') + end + + # Set some default inversion provider options. Package name can't get + # a default value here because that would complicate the handling of + # {system_package_candidates}. + # + # @api private + def default_inversion_options(node, resource) + super.merge({ + # Install dev headers? + dev_package: true, + # Manual overrides for package name and/or version. + package_name: nil, + package_version: nil, + # Set to true to use action :upgrade on system packages. + package_upgrade: false, + }) + end + + # @overload packages() + # Return a hash formatted for value_for_platform returning an Array + # of package names. + # @return [Hash] + # @overload packages(default_package, packages) + # Define what system packages are available for this language on each + # platform. + # @param default_package [String] Default package name for platforms + # not otherwise defined. + # @param [Hash] Hash formatted for value_for_platform returning an + # Array of package names. + # @return [Hash] + def packages(default_package=nil, packages=nil) + self.default_package(default_package) if default_package + if packages + @packages = packages + end + @packages + end + + # @overload default_package() + # Return the default package name for platforms not otherwise defined. + # @return [String] + # @overload default_package(name) + # Set the default package name for platforms not defined in {packages}. + # @param name [String] Package name. + # @return [String] + def default_package(name=nil) + if name + @default_package = name + end + @default_package + end + + # @api private + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + + end + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/system/resource.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/system/resource.rb new file mode 100644 index 0000000..3e40f52 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/system/resource.rb @@ -0,0 +1,215 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'chef/provider' +require 'poise' + + +module PoiseLanguages + module System + # A `poise_language_system` resource to manage installing a language from + # system packages. This is an internal implementation detail of + # poise-languages. + # + # @api private + # @since 1.0 + # @provides poise_languages_system + # @action install + # @action upgrade + # @action uninstall + class Resource < Chef::Resource + include Poise + provides(:poise_languages_system) + actions(:install, :upgrade, :uninstall) + + # @!attribute package_name + # Name of the main package for the language. + # @return [String] + attribute(:package_name, kind_of: String, name_attribute: true) + # @!attribute dev_package + # Name of the development headers package, or false to disable + # installing headers. By default computed from {package_name}. + # @return [String, false] + attribute(:dev_package, kind_of: [String, FalseClass], default: lazy { default_dev_package }) + # @!attribute dev_package_overrides + # A hash of override names for dev packages that don't match the normal + # naming scheme. + # @return [Hash] + attribute(:dev_package_overrides, kind_of: Hash, default: lazy { {} }) + # @!attribute package_version + # Version of the package(s) to install. This is distinct from {version}, + # and is the specific version package version, not the language version. + # By default this is unset meaning the latest version will be used. + # @return [String, nil] + attribute(:package_version, kind_of: [String, NilClass]) + # @!attribute parent + # Resource for the language runtime. Used only for messages. + # @return [Chef::Resource] + attribute(:parent, kind_of: Chef::Resource, required: true) + # @!attributes version + # Language version prefix. This prefix determines which version of the + # language to install, following prefix matching rules. + # @return [String] + attribute(:version, kind_of: String, default: '') + + # Compute the default package name for the development headers. + # + # @return [String] + def default_dev_package + # Check for an override. + return dev_package_overrides[package_name] if dev_package_overrides.include?(package_name) + suffix = node.value_for_platform_family(debian: '-dev', rhel: '-devel', fedora: '-devel') + # Platforms like Arch and Gentoo don't need this anyway. I've got no + # clue how Amazon Linux does this. + if suffix + package_name + suffix + else + nil + end + end + end + + # The default provider for `poise_languages_system`. + # + # @api private + # @since 1.0 + # @see Resource + # @provides poise_languages_system + class Provider < Chef::Provider + include Poise + provides(:poise_languages_system) + + # The `install` action for the `poise_languages_system` resource. + # + # @return [void] + def action_install + run_package_action(:install) + end + + # The `upgrade` action for the `poise_languages_system` resource. + # + # @return [void] + def action_upgrade + run_package_action(:upgrade) + end + + # The `uninstall` action for the `poise_languages_system` resource. + # + # @return [void] + def action_uninstall + action = node.platform_family?('debian') ? :purge : :remove + package_resources(action).each do |resource| + resource.run_action(action) + new_resource.updated_by_last_action(true) if resource.updated_by_last_action? + end + end + + private + + # Create package resource objects for all needed packages. These are created + # directly and not added to the resource collection. + # + # @return [Array] + def package_resources(action) + packages = {new_resource.package_name => new_resource.package_version} + # If we are supposed to install the dev package, grab it using the same + # version as the main package. + if new_resource.dev_package + packages[new_resource.dev_package] = new_resource.package_version + end + + Chef::Log.debug("[#{new_resource.parent}] Building package resource using #{packages.inspect}.") + package_resource_class = Chef::Resource.resource_for_node(:package, node) + @package_resource ||= if node.platform_family?('rhel', 'fedora', 'amazon', 'mac_os_x') + # @todo Can't use multi-package mode with yum pending https://github.com/chef/chef/issues/3476. + packages.map do |name, version| + package_resource_class.new(name, run_context).tap do |r| + r.version(version) + r.action(action) + r.declared_type = :package + r.retries(5) + end + end + else + [package_resource_class.new(packages.keys, run_context).tap do |r| + r.version(packages.values) + r.action(action) + r.declared_type = :package + r.retries(5) + end] + end + end + + # Run the requested action for all package resources. This exists because + # we inject our version check in to the provider directly and I want to + # only run the provider action once for performance. It is otherwise + # mostly a stripped down version of Chef::Resource#run_action. + # + # @param action [Symbol] Action to run on all package resources. + # @return [void] + def run_package_action(action) + package_resources(action).each do |resource| + # Reset it so we have a clean baseline. + resource.updated_by_last_action(false) + # Grab the provider. + provider = resource.provider_for_action(action) + provider.action = action + # Check the candidate version if needed. With a manual package_version + # you get whatever you asked for. + patch_load_current_resource!(provider, new_resource.version) unless new_resource.package_version + # Run our action. + Chef::Log.debug("[#{new_resource.parent}] Running #{provider} with #{action}") + provider.run_action(action) + # Check updated flag. + new_resource.updated_by_last_action(true) if resource.updated_by_last_action? + end + end + + # Hack a provider object to run our verification code. + # + # @param provider [Chef::Provider] Provider object to patch. + # @param version [String] Language version prefix to check for. + # @return [void] + def patch_load_current_resource!(provider, version) + # Create a closure module and inject it. + provider.extend Module.new { + # Patch load_current_resource to run our verification logic after + # the normal code. + define_method(:load_current_resource) do + super().tap do |_| + each_package do |package_name, new_version, current_version, candidate_version| + # In Chef 12.14+, candidate_version is a Chef::Decorator::Lazy object + # so we need the nil? check to see if the object being proxied is + # nil (i.e. there is no version). + unless candidate_version && (!candidate_version.nil?) && (!candidate_version.empty?) && candidate_version.start_with?(version) + # Don't display a wonky error message if there is no candidate. + candidate_label = if candidate_version && (!candidate_version.nil?) && (!candidate_version.empty?) + candidate_version + else + candidate_version.inspect + end + raise PoiseLanguages::Error.new("Package #{package_name} would install #{candidate_label}, which does not match #{version.empty? ? version.inspect : version}. Please set the package_name or package_version provider options.") + end + end + end + end + } + end + + end + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/utils.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/utils.rb new file mode 100644 index 0000000..84ff73d --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/utils.rb @@ -0,0 +1,68 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'shellwords' + +require 'poise_languages/utils/which' + + +module PoiseLanguages + module Utils + include Which + extend self + + # Default whitelist for {#shelljoin}. + SHELLJOIN_WHITELIST = [/^2?[><]/] + + # An improved version of Shellwords.shelljoin that doesn't escape a few + # things. + # + # @param cmd [Array] Command array to join. + # @param whitelist [Array] Array of patterns to whitelist. + # @return [String] + def shelljoin(cmd, whitelist: SHELLJOIN_WHITELIST) + cmd.map do |str| + if whitelist.any? {|pat| str =~ pat } + str + else + Shellwords.shellescape(str) + end + end.join(' ') + end + + # Convert the executable in a string or array command to an absolute path. + # + # @param cmd [String, Array] Command to fix up. + # @param path [String, nil] Replacement $PATH for executable lookup. + # @return [String, Array] + def absolute_command(cmd, path: nil) + was_array = cmd.is_a?(Array) + cmd = if was_array + cmd.dup + else + Shellwords.split(cmd) + end + # Don't try to touch anything if the first value looks like a flag or a path. + if cmd.first && !cmd.first.start_with?('-') && !cmd.first.include?(::File::SEPARATOR) + # If which returns false, just leave it I guess. + cmd[0] = which(cmd.first, path: path) || cmd.first + end + cmd = shelljoin(cmd) unless was_array + cmd + end + + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/utils/which.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/utils/which.rb new file mode 100644 index 0000000..8661c57 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/utils/which.rb @@ -0,0 +1,51 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseLanguages + module Utils + # Replacement module for Chef::Mixin::Which with a slight improvement. + # + # @since 1.0.0 + # @see Which#which + module Which + extend self + + # A replacement for Chef::Mixin::Which#which that allows using something + # other than an environment variable if needed. + # + # @param cmd [String] Executable to search for. + # @param extra_path [Array] Extra directories to always search. + # @param path [String, nil] Replacement $PATH value. + # @return [String, false] + def which(cmd, extra_path: %w{/bin /usr/bin /sbin /usr/sbin}, path: nil) + # If it was already absolute, just return that. + return cmd if cmd =~ /^(\/|([a-z]:)?\\)/i + # Allow passing something other than the real env var. + path ||= ENV['PATH'] + # Based on Chef::Mixin::Which#which + # Copyright 2010-2017, Chef Softare, Inc. + paths = path.split(File::PATH_SEPARATOR) + extra_path + paths.each do |candidate_path| + filename = ::File.join(candidate_path, cmd) + return filename if ::File.executable?(filename) + end + false + end + + end + end +end diff --git a/cookbooks/poise-languages/files/halite_gem/poise_languages/version.rb b/cookbooks/poise-languages/files/halite_gem/poise_languages/version.rb new file mode 100644 index 0000000..916a6c0 --- /dev/null +++ b/cookbooks/poise-languages/files/halite_gem/poise_languages/version.rb @@ -0,0 +1,20 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseLanguages + VERSION = '2.1.0' +end diff --git a/cookbooks/poise-languages/libraries/default.rb b/cookbooks/poise-languages/libraries/default.rb new file mode 100644 index 0000000..8b2a908 --- /dev/null +++ b/cookbooks/poise-languages/libraries/default.rb @@ -0,0 +1,18 @@ +# +# Copyright 2015-2017, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) diff --git a/cookbooks/poise-languages/metadata.json b/cookbooks/poise-languages/metadata.json new file mode 100644 index 0000000..7d99ddf --- /dev/null +++ b/cookbooks/poise-languages/metadata.json @@ -0,0 +1 @@ +{"name":"poise-languages","version":"2.1.0","description":"A Chef cookbook to help writing language cookbooks.","long_description":"# Poise-Languages Cookbook\n\n[![Build Status](https://img.shields.io/travis/poise/poise-languages.svg)](https://travis-ci.org/poise/poise-languages)\n[![Gem Version](https://img.shields.io/gem/v/poise-languages.svg)](https://rubygems.org/gems/poise-languages)\n[![Cookbook Version](https://img.shields.io/cookbook/v/poise-languages.svg)](https://supermarket.chef.io/cookbooks/poise-languages)\n[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-languages.svg)](https://codecov.io/github/poise/poise-languages)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-languages.svg)](https://gemnasium.com/poise/poise-languages)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nShared support code for Poise's language cookbooks like poise-ruby and\npoise-python.\n\n## License\n\nCopyright 2015-2017, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainer":"Noah Kantrowitz","maintainer_email":"noah@coderanger.net","license":"Apache 2.0","platforms":{},"dependencies":{"poise":"~> 2.5","poise-archive":"~> 1.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{},"source_url":"https://github.com/poise/poise-languages","issues_url":"https://github.com/poise/poise-languages/issues","chef_version":[["< 14",">= 12.1"],[]],"ohai_version":[[]]} \ No newline at end of file diff --git a/cookbooks/poise-ruby/CHANGELOG.md b/cookbooks/poise-ruby/CHANGELOG.md new file mode 100644 index 0000000..7145774 --- /dev/null +++ b/cookbooks/poise-ruby/CHANGELOG.md @@ -0,0 +1,25 @@ +# Changelog + +## v2.2.0 + +* Add support for Ubuntu 16.04 system packages. +* Support new SCL structure and packages. + +## v2.1.1 + +* Create ChefSpec matchers for the `ruby_gem` resource. + +## v2.1.0 + +* Fix version field for default Ruby runtime. +* Add a `:dummy` provider for `ruby_runtime` for unit testing or complex overrides. +* Improved handling for `bundle exec` in `ruby_execute`. +* New integration test harness. + +## v2.0.0 + +* Initial release (again)! + +## v1.0.0 + +* Pre-history, we do not speak of these times. diff --git a/cookbooks/poise-ruby/README.md b/cookbooks/poise-ruby/README.md new file mode 100644 index 0000000..efc01a6 --- /dev/null +++ b/cookbooks/poise-ruby/README.md @@ -0,0 +1,305 @@ +# Poise-Ruby Cookbook + +[![Build Status](https://img.shields.io/travis/poise/poise-ruby.svg)](https://travis-ci.org/poise/poise-ruby) +[![Gem Version](https://img.shields.io/gem/v/poise-ruby.svg)](https://rubygems.org/gems/poise-ruby) +[![Cookbook Version](https://img.shields.io/cookbook/v/poise-ruby.svg)](https://supermarket.chef.io/cookbooks/poise-ruby) +[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-ruby.svg)](https://codecov.io/github/poise/poise-ruby) +[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-ruby.svg)](https://gemnasium.com/poise/poise-ruby) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +A [Chef](https://www.chef.io/) cookbook to provide a unified interface for +installing Ruby and running things with it. This README covers the 2.x version +of the cookbook, the 1.x version is very different and no longer supported. + +## Quick Start + +To install the latest available version of Ruby 2.x and then use it to install +some gems: + +```ruby +ruby_runtime '2' + +ruby_gem 'rake' + +bundle_install '/path/to/Gemfile' do + without 'development' + deployment true +end +``` + +## Requirements + +Chef 12.1 or newer is required. + +## Attributes + +Attributes are used to configure the default recipe. + +* `node['poise-ruby']['install_ruby']` – Install a Ruby runtime. *(default: true)* +* `node['poise-ruby']['install_chef_ruby']` – Create a `ruby_runtime` using + the `:chef` provider. Doesn't actually install anything. *(default: true)* + +## Recipes + +### `default` + +The default recipe installs Ruby based on the node attributes. It is entirely +optional and can be ignored in favor of direct use of the `ruby_runtime` +resource. + +## Resources + +### `ruby_runtime` + +The `ruby_runtime` resource installs a Ruby interpreter. + +```ruby +ruby_runtime 'any' do + version '' +end +``` + +#### Actions + +* `:install` – Install the Ruby interpreter. *(default)* +* `:uninstall` – Uninstall the Ruby interpreter. + +#### Properties + +* `version` – Version of Ruby to install. If a partial version is given, use the + latest available version matching that prefix. *(name properties)* + +#### Provider Options + +The `poise-ruby` library offers an additional way to pass configuration +information to the final provider called "options". Options are key/value pairs +that are passed down to the ruby_runtime provider and can be used to control how it +installs Ruby. These can be set in the `ruby_runtime` +resource using the `options` method, in node attributes or via the +`ruby_runtime_options` resource. The options from all sources are merged +together in to a single hash. + +When setting options in the resource you can either set them for all providers: + +```ruby +ruby_runtime 'myapp' do + version '2.1' + options dev_package: false +end +``` + +or for a single provider: + +```ruby +ruby_runtime 'myapp' do + version '2.1' + options :system, dev_package: false +end +``` + +Setting via node attributes is generally how an end-user or application cookbook +will set options to customize installations in the library cookbooks they are using. +You can set options for all installations or for a single runtime: + +```ruby +# Global, for all installations. +override['poise-ruby']['options']['dev_package'] = false +# Single installation. +override['poise-ruby']['myapp']['version'] = '2.2' +``` + +The `ruby_runtime_options` resource is also available to set node attributes +for a specific installation in a DSL-friendly way: + +```ruby +ruby_runtime_options 'myapp' do + version '2.2' +end +``` + +Unlike resource attributes, provider options can be different for each provider. +Not all providers support the same options so make sure to the check the +documentation for each provider to see what options the use. + +### `ruby_runtime_options` + +The `ruby_runtime_options` resource allows setting provider options in a +DSL-friendly way. See [the Provider Options](#provider-options) section for more +information about provider options overall. + +```ruby +ruby_runtime_options 'myapp' do + version '2.2' +end +``` + +#### Actions + +* `:run` – Apply the provider options. *(default)* + +#### Properties + +* `resource` – Name of the `ruby_runtime` resource. *(name property)* +* `for_provider` – Provider to set options for. + +All other property keys will be used as options data. + +### `ruby_execute` + +The `ruby_execute` resource executes a Ruby script using the configured runtime. + +```ruby +ruby_execute 'myapp.rb' do + user 'myuser' +end +``` + +This uses the built-in `execute` resource and supports all the same properties. + +#### Actions + +* `:run` – Execute the script. *(default)* + +#### Properties + +* `command` – Script and arguments to run. Must not include the `ruby`. *(name property)* +* `ruby` – Name of the `ruby_runtime` resource to use. If not specified, the + most recently declared `ruby_runtime` will be used. + +For other properties see the [Chef documentation](https://docs.chef.io/resource_execute.html#attributes). + +### `ruby_gem` + +The `ruby_gem` resource is a subclass of the standard `gem_package` resource to +install the gem with the configured runtime. + +```ruby +ruby_gem 'rake' do + version ' 10.4.2' +end +``` + +All actions and attributes match the standard `gem_package` resource with the +addition of a `ruby` attribute matching `ruby_execute`. + +### `bundle_install` + +The `bundle_install` resource installs gems based on a Gemfile using +[bundler](http://bundler.io/). + +```ruby +bundle_install '/path/to/Gemfile' do + deployment true + jobs 3 +end +``` + +The underlying `bundle` command will run on every converge, but notifications +will only be triggered if a gem is actually installed. + +#### Actions + +* `:install` – Run `bundle install`. *(default)* +* `:update` – Run `bundle update`. + +#### Properties + +* `path` – Path to a Gemfile or a directory containing a Gemfile. *(name property)* +* `binstubs` – Enable binstubs. If set to a string it is the path to generate + stubs in. +* `bundler_version` – Version of bundler to install. If unset the latest version is used. +* `deployment` – Enable deployment mode. +* `gem_binary` – Path to the gem binary. If unset this uses the `ruby_runtime` parent. +* `jobs` – Number of parallel installations to run. +* `retry` – Number of times to retry failed installations. +* `ruby` – Name of the `ruby_runtime` resource to execute against. +* `user` – User to run bundler as. +* `vendor` – Enable local vendoring. This maps to the `--path` option in bundler, + but that attribute name is already used. +* `without` – Group or groups to not install. + +## Ruby Providers + +### `system` + +The `system` provider installs Ruby using system packages. This is currently +only tested on platforms using `apt-get` and `yum` (Debian, Ubuntu, RHEL, CentOS +Amazon Linux, and Fedora) and is the default provider on those platforms. It +may work on other platforms but is untested. + +```ruby +ruby_runtime 'myapp' do + provider :system + version '2.1' +end +``` + +#### Options + +* `dev_package` – Install the package with the headers and other development + files. *(default: true)* +* `rubygems_package` – Install rubygems from a package. This is only needed for + Ruby 1.8. *(default: true on RHEL 6)* +* `package_name` – Override auto-detection of the package name. +* `package_upgrade` – Install using action `:upgrade`. *(default: false)* +* `package_version` – Override auto-detection of the package version. +* `version` – Override the Ruby version. + +### `scl` + +The `scl` provider installs Ruby using the [Software Collections](https://www.softwarecollections.org/) +packages. This is only available on RHEL and CentOS. SCL offers more +recent versions of Ruby than the system packages for the most part. If an SCL +package exists for the requested version, it will be used in preference to the +`system` provider. + +```ruby +ruby_runtime 'myapp' do + provider :scl + version '2.2' +end +``` + +### `chef` + +The `chef` provider uses the Ruby environment included in the Omnibus packages. +Great care should be taken when using this provider. + +```ruby +ruby_runtime 'myapp' do + provider :chef + version '2.1' +end +``` + +#### Options + +* `version` – Override the Ruby version. + +### `ruby_build` + +The `ruby_build` provider uses [ruby-build](https://github.com/sstephenson/ruby-build) +to compile and install Ruby. It can be found in the +[poise-ruby-build cookbook](https://github.com/poise/poise-ruby-build). + +## Sponsors + +Development sponsored by [Bloomberg](http://www.bloomberg.com/company/technology/). + +The Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/). + +## License + +Copyright 2015-2016, Noah Kantrowitz + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cookbooks/poise-ruby/attributes/default.rb b/cookbooks/poise-ruby/attributes/default.rb new file mode 100644 index 0000000..2d66e51 --- /dev/null +++ b/cookbooks/poise-ruby/attributes/default.rb @@ -0,0 +1,23 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Default inversion options. +default['poise-ruby']['provider'] = 'auto' +default['poise-ruby']['options'] = {} + +# Used for the default recipe. +default['poise-ruby']['install_ruby'] = true +default['poise-ruby']['install_chef_ruby'] = true diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby.rb new file mode 100644 index 0000000..3e258ee --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby.rb @@ -0,0 +1,25 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseRuby + autoload :BundlerMixin, 'poise_ruby/bundler_mixin' + autoload :Error, 'poise_ruby/error' + autoload :Resources, 'poise_ruby/resources' + autoload :RubyCommandMixin, 'poise_ruby/ruby_command_mixin' + autoload :RubyProviders, 'poise_ruby/ruby_providers' + autoload :VERSION, 'poise_ruby/version' +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/bundler_mixin.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/bundler_mixin.rb new file mode 100644 index 0000000..4dcc712 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/bundler_mixin.rb @@ -0,0 +1,84 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_ruby/error' + + +module PoiseRuby + # Mixin for creating bundle exec commands. + # + # @since 2.1.0 + module BundlerMixin + # Transform a command to run under `bundle exec` with the same semantics as + # Ruby execution elsewhere in this system. That means you should end up with + # something like `/bin/ruby /bin/bundle exec /bin/ruby /bin/cmd args`. + # + # @param cmd [String, Array] Command to transform. + # @param path [String] Optional input path for command resolution. + # @return [String, Array] + def bundle_exec_command(cmd, path: nil) + bundle = new_resource.parent_bundle + return cmd unless bundle + is_array = cmd.is_a?(Array) + cmd = Shellwords.split(cmd) unless is_array + root_path = ::File.expand_path('..', bundle.gemfile_path) + # Grab this once in case I need it for the extra path. + bundler_binary = bundle.bundler_binary + # This doesn't account for the potential of a .bundle/config created with + # settings that Chef doesn't know about. (╯°□°)╯︵ ┻━┻ + extra_path = if bundle.binstubs + bundle.binstubs == true ? 'bin' : bundle.binstubs + elsif bundle.vendor || bundle.deployment + # Find the relative path to start searching from. + vendor_base_path = if bundle.vendor && bundle.vendor != true + bundle.vendor + else + 'vendor/bundle' + end + # Add the ruby/. + vendor_base_path = ::File.join(File.expand_path(vendor_base_path, root_path), 'ruby') + # Find the version number folder inside that. + candidates = Dir.entries(vendor_base_path) + ruby_abi_folder = candidates.find {|name| name =~ /^\d\./ } + vendor_sub_path = if ruby_abi_folder + ::File.join(ruby_abi_folder, 'bin') + elsif candidates.include?('bin') + 'bin' + else + raise PoiseRuby::Error.new("Unable to find the vendor bin folder for #{vendor_base_path}: #{candidates.join(', ')}") + end + # Make the final path. + ::File.join(vendor_base_path, vendor_sub_path) + else + # The folder the bundler binary is in was the global gem executable dir. + ::File.dirname(bundler_binary) + end + # Resolve relative paths against Bundler.root. + extra_path = ::File.expand_path(extra_path, root_path) + # Create the full $PATH. + path ||= ENV['PATH'] + bundle_exec_path = extra_path + ::File::PATH_SEPARATOR + path + # Resolve the command + abs_cmd = PoiseLanguages::Utils.absolute_command(cmd, path: bundle_exec_path) + bundle_exec = [new_resource.ruby, bundler_binary, 'exec', new_resource.ruby] + abs_cmd + if is_array + bundle_exec + else + PoiseLanguages::Utils.shelljoin(bundle_exec) + end + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/cheftie.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/cheftie.rb new file mode 100644 index 0000000..474a2f4 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/cheftie.rb @@ -0,0 +1,18 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_ruby/resources' +require 'poise_ruby/ruby_providers' diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/error.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/error.rb new file mode 100644 index 0000000..34835c7 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/error.rb @@ -0,0 +1,21 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseRuby + class Error < ::Exception + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources.rb new file mode 100644 index 0000000..11b5710 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources.rb @@ -0,0 +1,29 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_ruby/resources/bundle_install' +require 'poise_ruby/resources/ruby_execute' +require 'poise_ruby/resources/ruby_gem' +require 'poise_ruby/resources/ruby_runtime' + + +module PoiseRuby + # Chef resources and providers for poise-ruby. + # + # @since 2.0.0 + module Resources + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/bundle_install.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/bundle_install.rb new file mode 100644 index 0000000..52b6fe8 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/bundle_install.rb @@ -0,0 +1,221 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/shell_out' +require 'chef/mixin/which' +require 'chef/provider' +require 'chef/resource' +require 'poise' + +require 'poise_ruby/error' +require 'poise_ruby/ruby_command_mixin' + + +module PoiseRuby + module Resources + # (see BundleInstall::Resource) + # @since 2.0.0 + module BundleInstall + # A `bundle_install` resource to install a [Bundler](http://bundler.io/) + # Gemfile. + # + # @provides bundle_install + # @action install + # @action update + # @note + # This resource is not idempotent itself, it will always run `bundle + # install`. + # @example + # bundle_install '/opt/my_app' do + # gem_path '/usr/local/bin/gem' + # end + class Resource < Chef::Resource + include Poise + provides(:bundle_install) + actions(:install, :update) + include PoiseRuby::RubyCommandMixin + + # @!attribute path + # Path to the Gemfile or to a directory that contains a Gemfile. + # @return [String] + attribute(:path, kind_of: String, name_attribute: true) + # @!attribute binstubs + # Enable binstubs. If set to a string it is the path to generate + # stubs in. + # @return [Boolean, String] + attribute(:binstubs, kind_of: [TrueClass, String]) + # @!attribute deployment + # Enable deployment mode. + # @return [Boolean] + attribute(:deployment, equal_to: [true, false], default: false) + # @!attribute jobs + # Number of parallel installations to run. + # @return [String, Integer] + attribute(:jobs, kind_of: [String, Integer]) + # @!attribute retry + # Number of times to retry failed installations. + # @return [String, Integer] + attribute(:retry, kind_of: [String, Integer]) + # @!attribute user + # User to run bundler as. + # @return [String, Integery, nil] + attribute(:user, kind_of: [String, Integer, NilClass]) + # @!attribute vendor + # Enable local vendoring. This maps to the `--path` option in bundler, + # but that attribute name is already used. + # @return [Boolean, String] + attribute(:vendor, kind_of: [TrueClass, String]) + # @!attribute without + # Group or groups to not install. + # @return [String, Array] + attribute(:without, kind_of: [Array, String]) + + # The path to the `bundle` binary for this installation. This is an + # output property. + # + # @return [String] + # @example + # execute "#{resources('bundle_install[/opt/myapp]').bundler_binary} vendor" + def bundler_binary + @bundler_binary ||= provider_for_action(:bundler_binary).bundler_binary + end + + # The path to the Gemfile for this installation. This is an output + # property. + # + # @return [String] + # @example + # file resources('bundle_install[/opt/myapp]').gemfile_path do + # owner 'root' + # end + def gemfile_path + @gemfile_path ||= provider_for_action(:gemfile_path).gemfile_path + end + end + + # The default provider for the `bundle_install` resource. + # + # @see Resource + class Provider < Chef::Provider + include Poise + provides(:bundle_install) + include PoiseRuby::RubyCommandMixin + + # Install bundler and the gems in the Gemfile. + def action_install + run_bundler('install') + end + + # Install bundler and update the gems in the Gemfile. + def action_update + run_bundler('update') + end + + # Return the absolute path to the correct bundle binary to run. + # + # @return [String] + def bundler_binary + @bundler_binary ||= ::File.join(gem_bindir, 'bundle') + end + + # Find the absolute path to the Gemfile. This mirrors bundler's internal + # search logic by scanning up to parent folder as needed. + # + # @return [String] + def gemfile_path + @gemfile_path ||= begin + path = ::File.expand_path(new_resource.path) + if ::File.file?(path) + # We got a path to a real file, use that. + path + else + # Walk back until path==dirname(path) meaning we are at the root + while path != (next_path = ::File.dirname(path)) + possible_path = ::File.join(path, 'Gemfile') + return possible_path if ::File.file?(possible_path) + path = next_path + end + end + end + end + + private + + # Install the gems in the Gemfile. + def run_bundler(command) + return converge_by "Run bundle #{command}" if whyrun_mode? + cmd = ruby_shell_out!(bundler_command(command), environment: {'BUNDLE_GEMFILE' => gemfile_path}, user: new_resource.user) + # Look for a line like 'Installing $gemname $version' to know if we did anything. + if cmd.stdout.include?('Installing') + new_resource.updated_by_last_action(true) + end + end + + # Parse out the value for Gem.bindir. This is so complicated to minimize + # the required configuration on the resource combined with gem having + # terrible output formats. + # + # @return [String] + def gem_bindir + cmd = ruby_shell_out!(new_resource.gem_binary, 'environment') + # Parse a line like: + # - EXECUTABLE DIRECTORY: /usr/local/bin + matches = cmd.stdout.scan(/EXECUTABLE DIRECTORY: (.*)$/).first + if matches + matches.first + else + raise PoiseRuby::Error.new("Cannot find EXECUTABLE DIRECTORY: #{cmd.stdout}") + end + end + + # Command line options for the bundle install. + # + # @return [Array] + def bundler_options + [].tap do |opts| + if new_resource.binstubs + opts << "--binstubs" + (new_resource.binstubs.is_a?(String) ? "=#{new_resource.binstubs}" : '') + end + if new_resource.vendor + opts << "--path=" + (new_resource.vendor.is_a?(String) ? new_resource.vendor : 'vendor/bundle') + end + if new_resource.deployment + opts << '--deployment' + end + if new_resource.jobs + opts << "--jobs=#{new_resource.jobs}" + end + if new_resource.retry + opts << "--retry=#{new_resource.retry}" + end + if new_resource.without + opts << '--without' + opts.insert(-1, *new_resource.without) + end + end + end + + # Command array to run when installing the Gemfile. + # + # @return [Array] + def bundler_command(command) + [bundler_binary, command] + bundler_options + end + + end + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_execute.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_execute.rb new file mode 100644 index 0000000..47ce0e0 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_execute.rb @@ -0,0 +1,90 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mash' +require 'chef/provider/execute' +require 'chef/resource/execute' +require 'poise' + +require 'poise_ruby/bundler_mixin' +require 'poise_ruby/ruby_command_mixin' + + +module PoiseRuby + module Resources + # (see RubyExecute::Resource) + # @since 2.0.0 + module RubyExecute + # A `ruby_execute` resource to run Ruby scripts and commands. + # + # @provides ruby_execute + # @action run + # @example + # ruby_execute 'myapp.rb' do + # user 'myuser' + # end + class Resource < Chef::Resource::Execute + include Poise + provides(:ruby_execute) + actions(:run) + include PoiseRuby::RubyCommandMixin + + # @!attribute parent_bundle + # Optional bundle_install resource to run `bundle exec` against. + # @return [PoiseRuby::Resources::BundleInstall::Resource] + parent_attribute(:bundle, type: :bundle_install, optional: true, auto: false) + end + + # The default provider for `ruby_execute`. + # + # @see Resource + # @provides ruby_execute + class Provider < Chef::Provider::Execute + include PoiseRuby::BundlerMixin + provides(:ruby_execute) + + private + + # Command to pass to shell_out. + # + # @return [String, Array] + def command + if new_resource.parent_bundle + bundle_exec_command(new_resource.command, path: environment['PATH']) + else + if new_resource.command.is_a?(Array) + [new_resource.ruby] + new_resource.command + else + "#{new_resource.ruby} #{new_resource.command}" + end + end + end + + # Environment variables to pass to shell_out. + # + # @return [Hash] + def environment + Mash.new.tap do |environment| + environment.update(new_resource.parent_ruby.ruby_environment) if new_resource.parent_ruby + environment['BUNDLE_GEMFILE'] = new_resource.parent_bundle.gemfile_path if new_resource.parent_bundle + environment.update(new_resource.environment) if new_resource.environment + end + end + + end + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_gem.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_gem.rb new file mode 100644 index 0000000..26ac898 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_gem.rb @@ -0,0 +1,125 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider/package/rubygems' +require 'chef/resource/gem_package' +require 'poise' + +require 'poise_ruby/ruby_command_mixin' + + +module PoiseRuby + module Resources + # (see RubyGem::Resource) + # @since 2.0.0 + module RubyGem + # A `ruby_gem` resource to install Ruby gems. + # + # @provides ruby_gem + # @action install + # @action upgrade + # @action remove + # @action purge + # @action reconfig + # @example + # ruby_gem 'rack' + class Resource < Chef::Resource::GemPackage + include Poise + provides(:ruby_gem) + actions(:install, :upgrade, :remove, :purge, :reconfig) + include PoiseRuby::RubyCommandMixin + + # @api private + def initialize(name, run_context=nil) + super + @resource_name = :ruby_gem if @resource_name + # Remove when all useful versions are using provider resolver. + @provider = PoiseRuby::Resources::RubyGem::Provider if @provider + end + end + + # The default provider for `ruby_gem`. + # + # @see Resource + # @provides ruby_gem + class Provider < Chef::Provider::Package::Rubygems + include Poise + provides(:ruby_gem) + + def load_current_resource + patch_environment { super } + end + + def define_resource_requirements + patch_environment { super } + end + + def action_install + patch_environment { super } + end + + def action_upgrade + patch_environment { super } + end + + def action_remove + patch_environment { super } + end + + def action_purge + patch_environment { super } + end + + def action_reconfig + patch_environment { super } + end + + private + + def patch_environment(&block) + environment_to_add = if new_resource.parent_ruby + new_resource.parent_ruby.ruby_environment + else + {} + end + + begin + if ENV['GEM_HOME'] && !ENV['GEM_HOME'].empty? + Chef::Log.warn("[#{new_resource}] $GEM_HOME is set in Chef's environment, this will likely interfere with gem installation") + end + if ENV['GEM_PATH'] && !ENV['GEM_PATH'].empty? + Chef::Log.warn("[#{new_resource}] $GEM_PATH is set in Chef's environment, this will likely interfere with gem installation") + end + old_vars = environment_to_add.inject({}) do |memo, (key, value)| + memo[key] = ENV[key] + ENV[key] = value + memo + end + block.call + ensure + old_vars.each do |key, value| + if value.nil? + ENV.delete(key) + else + ENV[key] = value + end + end + end + end + end + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_runtime.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_runtime.rb new file mode 100644 index 0000000..e0b0292 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_runtime.rb @@ -0,0 +1,87 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'poise' + + +module PoiseRuby + module Resources + # (see RubyRuntime::Resource) + # @since 2.0.0 + module RubyRuntime + # A `ruby_runtime` resource to manage Ruby installations. + # + # @provides ruby_runtime + # @action install + # @action uninstall + # @example + # ruby_runtime '2.1.2' + class Resource < Chef::Resource + include Poise(inversion: true, container: true) + provides(:ruby_runtime) + actions(:install, :uninstall) + + # @!attribute version + # Version of Ruby to install. + # @return [String] + attribute(:version, kind_of: String, name_attribute: true) + # @!attribute bundler_version + # Version of Bundler to install. It set to `true`, the latest + # available version will be used. If set to `false`, Bundler will + # not be installed. + # @note Disabling the Bundler install may result in other resources + # being non-functional. + # @return [String, Boolean] + attribute(:bundler_version, kind_of: [String, TrueClass, FalseClass], default: true) + + # The path to the `ruby` binary for this Ruby installation. This is an + # output property. + # + # @return [String] + # @example + # execute "#{resources('ruby_runtime[2.2.2]').ruby_binary} myapp.rb" + def ruby_binary + @ruby_binary ||= provider_for_action(:ruby_binary).ruby_binary + end + + # The environment variables for this Ruby installation. This is an + # output property. + # + # @return [Hash] + # @example + # execute '/opt/myapp.py' do + # environment resources('ruby_runtime[2.2.2]').ruby_environment + # end + def ruby_environment + @ruby_environment ||= provider_for_action(:ruby_environment).ruby_environment + end + + # The path to the `gem` binary for this Ruby installation. This is an + # output property. + # + # @return [String] + # @example + # execute "#{resources('ruby_runtime[2.2.2]').gem_binary} install myapp" + def gem_binary + @gem_binary ||= provider_for_action(:gem_binary).gem_binary + end + end + + # Providers can be found under lib/poise_ruby/ruby_providers/ + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_runtime_test.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_runtime_test.rb new file mode 100644 index 0000000..34ae7da --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/resources/ruby_runtime_test.rb @@ -0,0 +1,213 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/convert_to_class_name' +require 'chef/provider' +require 'chef/resource' +require 'poise' + + +module PoiseRuby + module Resources + # (see RubyRuntimeTest::Resource) + # @since 2.1.0 + # @api private + module RubyRuntimeTest + # A `ruby_runtime_test` resource for integration testing of this + # cookbook. This is an internal API and can change at any time. + # + # @provides ruby_runtime_test + # @action run + class Resource < Chef::Resource + include Poise + provides(:ruby_runtime_test) + actions(:run) + + attribute(:version, kind_of: String, name_attribute: true) + attribute(:runtime_provider, kind_of: Symbol) + attribute(:path, kind_of: String, default: lazy { default_path }) + + def default_path + ::File.join('', 'root', "ruby_test_#{name}") + end + end + + # The default provider for `ruby_runtime_test`. + # + # @see Resource + # @provides ruby_runtime_test + class Provider < Chef::Provider + include Poise + provides(:ruby_runtime_test) + + # The `run` action for the `ruby_runtime_test` resource. + # + # @return [void] + def action_run + notifying_block do + # Top level directory for this test. + directory new_resource.path + + # Install and log the version. + ruby_runtime new_resource.name do + provider new_resource.runtime_provider if new_resource.runtime_provider + version new_resource.version + end + test_version + + # Test ruby_gem. + ruby_gem 'thor remove before' do + action :remove + package_name 'thor' + ruby new_resource.name + end + test_require('thor', 'thor_before') + ruby_gem 'thor' do + ruby new_resource.name + notifies :create, sentinel_file('thor'), :immediately + end + test_require('thor', 'thor_mid') + ruby_gem 'thor again' do + package_name 'thor' + ruby new_resource.name + notifies :create, sentinel_file('thor2'), :immediately + end + ruby_gem 'thor remove after' do + action :remove + package_name 'thor' + ruby new_resource.name + end + test_require('thor', 'thor_after') + + # Use bundler to test something that should always be installed. + ruby_gem 'bundler' do + ruby new_resource.name + notifies :create, sentinel_file('bundler'), :immediately + end + + # Create and install a Gemfile. + bundle1_path = ::File.join(new_resource.path, 'bundle1') + directory bundle1_path + file ::File.join(bundle1_path, 'Gemfile') do + content <<-EOH +source 'https://rubygems.org/' +gem 'hashie' +gem 'tomlrb', '1.1.0' +EOH + end + bundle1 = bundle_install bundle1_path do + ruby new_resource.name + end + test_require('hashie', bundle: bundle1) + test_require('tomlrb', bundle: bundle1) + test_require('thor', 'thor_bundle', bundle: bundle1) + + # Test for bundle exec shebang issues. + bundle2_path = ::File.join(new_resource.path, 'bundle2') + directory bundle2_path + file ::File.join(bundle2_path, 'Gemfile') do + content <<-EOH +source 'https://rubygems.org/' +gem 'unicorn' +EOH + end + file ::File.join(bundle2_path, 'Gemfile.lock') do + content <<-EOH +GEM + remote: https://rubygems.org/ + specs: + kgio (2.10.0) + rack (1.6.4) + raindrops (0.15.0) + unicorn (4.9.0) + kgio (~> 2.6) + rack + raindrops (~> 0.7) + +PLATFORMS + ruby + +DEPENDENCIES + unicorn + +BUNDLED WITH + 1.10.6 +EOH + end + bundle2 = bundle_install bundle2_path do + ruby new_resource.name + deployment true + end + # test_require('unicorn', bundle: bundle2) + ruby_execute "unicorn --version > #{::File.join(new_resource.path, "unicorn_version")}" do + ruby new_resource.name + parent_bundle bundle2 + end + end + end + + def sentinel_file(name) + file ::File.join(new_resource.path, "sentinel_#{name}") do + action :nothing + end + end + + private + + def test_version(ruby: new_resource.name) + # Only queue up this resource once, the ivar is just for tracking. + @ruby_version_test ||= file ::File.join(new_resource.path, 'ruby_version.rb') do + user 'root' + group 'root' + mode '644' + content <<-EOH +File.new(ARGV[0], 'w').write(RUBY_VERSION) +EOH + end + + ruby_execute "#{@ruby_version_test.path} #{::File.join(new_resource.path, 'version')}" do + ruby ruby if ruby + end + end + + def test_require(name, path=name, ruby: new_resource.name, bundle: nil, class_name: nil) + # Only queue up this resource once, the ivar is just for tracking. + @ruby_require_test ||= file ::File.join(new_resource.path, 'require_version.rb') do + user 'root' + group 'root' + mode '644' + content <<-EOH +require 'rubygems' +begin + require "\#{ARGV[0]}/version" + klass = ARGV[1].split('::').inject(Object) {|memo, name| memo.const_get(name) } + File.new(ARGV[2], 'w').write(klass::VERSION) +rescue LoadError +end +EOH + end + + class_name ||= Chef::Mixin::ConvertToClassName.convert_to_class_name(name) + ruby_execute "#{@ruby_require_test.path} #{name} #{class_name} #{::File.join(new_resource.path, "require_#{path}")}" do + ruby ruby if ruby + parent_bundle bundle if bundle + end + end + + end + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_command_mixin.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_command_mixin.rb new file mode 100644 index 0000000..01a15b3 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_command_mixin.rb @@ -0,0 +1,59 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/utils' +require 'poise_languages' + + +module PoiseRuby + # Mixin for resources and providers which run Ruby commands. + # + # @since 2.0.0 + module RubyCommandMixin + include Poise::Utils::ResourceProviderMixin + + module Resource + include PoiseLanguages::Command::Mixin::Resource(:ruby) + + # @!attribute gem_binary + # Path to the gem binary. + # @return [String] + attribute(:gem_binary, kind_of: String, default: lazy { default_gem_binary }) + + private + + # Find the default gem binary. If there is a parent use that, otherwise + # use the same logic as {PoiseRuby::RubyProviders::Base#gem_binary}. + # + # @return [String] + def default_gem_binary + if parent_ruby + parent_ruby.gem_binary + else + dir, base = ::File.split(ruby) + # If this ruby is called something weird, bail out. + raise NotImplementedError unless base.start_with?('ruby') + # Allow for names like "ruby2.0" -> "gem2.0". + ::File.join(dir, base.sub(/^ruby/, 'gem')) + end + end + end + + module Provider + include PoiseLanguages::Command::Mixin::Provider(:ruby) + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers.rb new file mode 100644 index 0000000..7d742b5 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers.rb @@ -0,0 +1,35 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/platform/provider_priority_map' + +require 'poise_ruby/ruby_providers/chef' +require 'poise_ruby/ruby_providers/dummy' +require 'poise_ruby/ruby_providers/scl' +require 'poise_ruby/ruby_providers/system' + + +module PoiseRuby + # Inversion providers for the ruby_runtime resource. + # + # @since 2.0.0 + module RubyProviders + Chef::Platform::ProviderPriorityMap.instance.priority(:ruby_runtime, [ + PoiseRuby::RubyProviders::Scl, + PoiseRuby::RubyProviders::System, + ]) + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/base.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/base.rb new file mode 100644 index 0000000..ac7b3a9 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/base.rb @@ -0,0 +1,117 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'poise' + +require 'poise_ruby/resources/ruby_gem' +require 'poise_ruby/resources/ruby_runtime' + + +module PoiseRuby + module RubyProviders + class Base < Chef::Provider + include Poise(inversion: :ruby_runtime) + + # Set default inversion options. + # + # @api private + def self.default_inversion_options(node, new_resource) + super.merge({ + bundler_version: new_resource.bundler_version, + version: new_resource.version, + }) + end + + # The `install` action for the `ruby_runtime` resource. + # + # @return [void] + def action_install + notifying_block do + install_ruby + install_bundler + end + end + + # The `uninstall` action for the `ruby_runtime` resource. + # + # @return [void] + def action_uninstall + notifying_block do + uninstall_ruby + end + end + + # The path to the `ruby` binary. + # + # @abstract + # @return [String] + def ruby_binary + raise NotImplementedError + end + + # Output property for environment variables. + # + # @return [Hash] + def ruby_environment + # No environment variables needed. Rejoice. + {} + end + + # The path to the `gem` binary. Look relative to the + # `ruby` binary for a default implementation. + # + # @return [String] + def gem_binary + dir, base = ::File.split(ruby_binary) + # If this ruby is called something weird, bail out. + raise NotImplementedError unless base.start_with?('ruby') + # Allow for names like "ruby2.0" -> "gem2.0". + ::File.join(dir, base.sub(/^ruby/, 'gem')) + end + + private + + # Install the Ruby runtime. Must be implemented by subclass. + # + # @abstract + # @return [void] + def install_ruby + end + + # Uninstall the Ruby runtime. Must be implemented by subclass. + # + # @abstract + # @return [void] + def uninstall_ruby + end + + # Install Bundler in to the Ruby runtime. + # + # @return [void] + def install_bundler + # Captured because #options conflicts with Chef::Resource::Package#options. + bundler_version = options[:bundler_version] + return unless bundler_version + ruby_gem 'bundler' do + action :upgrade if bundler_version == true + parent_ruby new_resource + version bundler_version if bundler_version.is_a?(String) + end + end + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/chef.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/chef.rb new file mode 100644 index 0000000..259a0b8 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/chef.rb @@ -0,0 +1,53 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_ruby/error' +require 'poise_ruby/ruby_providers/base' + + +module PoiseRuby + module RubyProviders + # Inversion provider for the `ruby_runtime` resource to use whatever Ruby is + # currently running, generally Chef's omnibus-d Ruby. + # + # @since 2.0.0 + # @provides chef + class ChefRuby < Base + provides(:chef) + + # The `install` action for the `ruby_runtime` resource. + # + # @return [void] + def action_install + # No-op, already installed! + end + + # The `uninstall` action for the `ruby_runtime` resource. + # + # @return [void] + def action_uninstall + raise PoiseRuby::Error.new("You cannot uninstall Chef's Ruby.") + end + + # The path to the running Ruby binary as determined via RbConfig. + # + # @return [String] + def ruby_binary + Gem.ruby + end + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/dummy.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/dummy.rb new file mode 100644 index 0000000..3e8663f --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/dummy.rb @@ -0,0 +1,77 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_ruby/ruby_providers/base' + + +module PoiseRuby + module RubyProviders + # Inversion provider for the `ruby_runtime` resource to use a fake Ruby, + # for use in unit tests. + # + # @since 2.1.0 + # @provides dummy + class Dummy < Base + provides(:dummy) + + def self.default_inversion_options(node, resource) + super.merge({ + # Manual overrides for dummy data. + ruby_binary: ::File.join('', 'ruby'), + ruby_environment: nil, + gem_binary: nil, + }) + end + + # The `install` action for the `ruby_runtime` resource. + # + # @return [void] + def action_install + # This space left intentionally blank. + end + + # The `uninstall` action for the `ruby_runtime` resource. + # + # @return [void] + def action_uninstall + # This space left intentionally blank. + end + + # Path to the non-existent ruby. + # + # @return [String] + def ruby_binary + options['ruby_binary'] + end + + # Environment for the non-existent Ruby. + # + # @return [String] + def ruby_environment + options['ruby_environment'] || super + end + + # Path to the non-existent gem. + # + # @return [String] + def gem_binary + options['gem_binary'] || super + end + + end + end +end + diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/scl.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/scl.rb new file mode 100644 index 0000000..6d5b7d3 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/scl.rb @@ -0,0 +1,55 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'poise_languages' + +require 'poise_ruby/ruby_providers/base' + + +module PoiseRuby + module RubyProviders + class Scl < Base + include PoiseLanguages::Scl::Mixin + provides(:scl) + scl_package('2.3.0', 'rh-ruby23', 'rh-ruby23-ruby-devel', '>= 7.0') + scl_package('2.2.2', 'rh-ruby22', 'rh-ruby22-ruby-devel') + # On EL7, the system package is Ruby 2.0.0 and is newer than the SCL build. + scl_package('2.0.0', 'ruby200', 'ruby200-ruby-devel', '~> 6.0') + scl_package('1.9.3', 'ruby193', 'ruby193-ruby-devel') + + def ruby_binary + ::File.join(scl_folder, 'root', 'usr', 'bin', 'ruby') + end + + def ruby_environment + scl_environment + end + + private + + def install_ruby + install_scl_package + end + + def uninstall_ruby + uninstall_scl_package + end + + end + end +end + diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/system.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/system.rb new file mode 100644 index 0000000..945acf9 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/ruby_providers/system.rb @@ -0,0 +1,116 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'poise_languages' + +require 'poise_ruby/error' +require 'poise_ruby/ruby_providers/base' + + +module PoiseRuby + module RubyProviders + class System < Base + include PoiseLanguages::System::Mixin + provides(:system) + packages('ruby', { + debian: { + '8' => %w{ruby2.1}, + '7' => %w{ruby1.9.3 ruby1.9.1 ruby1.8}, + # Debian 6 has a ruby1.9.1 package that installs 1.9.2, ignoring it for now. + '6' => %w{ruby1.8}, + }, + ubuntu: { + '16.04' => %w{ruby2.3}, + '14.04' => %w{ruby2.0 ruby1.9.3}, + '12.04' => %w{ruby1.9.3 ruby1.8}, + '10.04' => %w{ruby1.9.1 ruby1.8}, + }, + rhel: {default: %w{ruby}}, + centos: {default: %w{ruby}}, + fedora: {default: %w{ruby}}, + # Amazon Linux does actually have packages ruby18, ruby19, ruby20, ruby21. + # Ignoring for now because wooooo non-standard formatting. + amazon: {default: %w{ruby}}, + }) + + def self.default_inversion_options(node, resource) + super.merge({ + # Install a separate rubygems package? Only needed for 1.8. + rubygems_package: node['platform_family'] == 'rhel' && node['platform_version'].start_with?('6'), + }) + end + + # Output value for the Python binary we are installing. Seems to match + # package name on all platforms I've checked. + def ruby_binary + ::File.join('', 'usr', 'bin', system_package_name) + end + + private + + def install_ruby + install_system_packages + install_rubygems_package if options['rubygems_package'] + end + + def uninstall_ruby + uninstall_system_packages + end + + # Ubuntu has no ruby1.9.3-dev package. + def system_dev_package_overrides + super.tap do |overrides| + # WTF Ubuntu, seriously. + overrides['ruby1.9.3'] = 'ruby1.9.1-dev' if node.platform_family?('debian') + end + end + + # Install the configured rubygems package. + def install_rubygems_package + package (options['rubygems_package'].is_a?(String) ? options['rubygems_package'] : 'rubygems') + end + + def system_package_candidates(version) + [].tap do |names| + # Might as well try it. + names << "ruby#{version}" if version && !['', '1', '2'].include?(version) + # On debian, 1.9.1 and 1.9.3 have special packages. + if match = version.match(/^(\d+\.\d+\.\d+)/) + names << "ruby#{match[1]}" + end + # Normal debian package like ruby2.0. + if match = version.match(/^(\d+\.\d+)/) + names << "ruby#{match[1]}" + end + # Aliases for ruby1 and ruby2 + if version == '2' || version == '' + # 2.3 is on there for future proofing. Well, at least giving me a + # buffer zone. + names.concat(%w{ruby2.3 ruby2.2 ruby2.1 ruby2.0}) + end + if version == '1' || version == '' + names.concat(%w{ruby1.9.3 ruby1.9 ruby1.8}) + end + # For RHEL and friends. + names << 'ruby' + names.uniq! + end + end + + end + end +end diff --git a/cookbooks/poise-ruby/files/halite_gem/poise_ruby/version.rb b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/version.rb new file mode 100644 index 0000000..41f6040 --- /dev/null +++ b/cookbooks/poise-ruby/files/halite_gem/poise_ruby/version.rb @@ -0,0 +1,20 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseRuby + VERSION = '2.2.0' +end diff --git a/cookbooks/poise-ruby/libraries/default.rb b/cookbooks/poise-ruby/libraries/default.rb new file mode 100644 index 0000000..2865254 --- /dev/null +++ b/cookbooks/poise-ruby/libraries/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) +require "poise_ruby/cheftie" diff --git a/cookbooks/poise-ruby/metadata.json b/cookbooks/poise-ruby/metadata.json new file mode 100644 index 0000000..d929224 --- /dev/null +++ b/cookbooks/poise-ruby/metadata.json @@ -0,0 +1 @@ +{"name":"poise-ruby","version":"2.2.0","description":"A Chef cookbook for managing Ruby installations.","long_description":"# Poise-Ruby Cookbook\n\n[![Build Status](https://img.shields.io/travis/poise/poise-ruby.svg)](https://travis-ci.org/poise/poise-ruby)\n[![Gem Version](https://img.shields.io/gem/v/poise-ruby.svg)](https://rubygems.org/gems/poise-ruby)\n[![Cookbook Version](https://img.shields.io/cookbook/v/poise-ruby.svg)](https://supermarket.chef.io/cookbooks/poise-ruby)\n[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-ruby.svg)](https://codecov.io/github/poise/poise-ruby)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-ruby.svg)](https://gemnasium.com/poise/poise-ruby)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nA [Chef](https://www.chef.io/) cookbook to provide a unified interface for\ninstalling Ruby and running things with it. This README covers the 2.x version\nof the cookbook, the 1.x version is very different and no longer supported.\n\n## Quick Start\n\nTo install the latest available version of Ruby 2.x and then use it to install\nsome gems:\n\n```ruby\nruby_runtime '2'\n\nruby_gem 'rake'\n\nbundle_install '/path/to/Gemfile' do\n without 'development'\n deployment true\nend\n```\n\n## Requirements\n\nChef 12.1 or newer is required.\n\n## Attributes\n\nAttributes are used to configure the default recipe.\n\n* `node['poise-ruby']['install_ruby']` – Install a Ruby runtime. *(default: true)*\n* `node['poise-ruby']['install_chef_ruby']` – Create a `ruby_runtime` using\n the `:chef` provider. Doesn't actually install anything. *(default: true)*\n\n## Recipes\n\n### `default`\n\nThe default recipe installs Ruby based on the node attributes. It is entirely\noptional and can be ignored in favor of direct use of the `ruby_runtime`\nresource.\n\n## Resources\n\n### `ruby_runtime`\n\nThe `ruby_runtime` resource installs a Ruby interpreter.\n\n```ruby\nruby_runtime 'any' do\n version ''\nend\n```\n\n#### Actions\n\n* `:install` – Install the Ruby interpreter. *(default)*\n* `:uninstall` – Uninstall the Ruby interpreter.\n\n#### Properties\n\n* `version` – Version of Ruby to install. If a partial version is given, use the\n latest available version matching that prefix. *(name properties)*\n\n#### Provider Options\n\nThe `poise-ruby` library offers an additional way to pass configuration\ninformation to the final provider called \"options\". Options are key/value pairs\nthat are passed down to the ruby_runtime provider and can be used to control how it\ninstalls Ruby. These can be set in the `ruby_runtime`\nresource using the `options` method, in node attributes or via the\n`ruby_runtime_options` resource. The options from all sources are merged\ntogether in to a single hash.\n\nWhen setting options in the resource you can either set them for all providers:\n\n```ruby\nruby_runtime 'myapp' do\n version '2.1'\n options dev_package: false\nend\n```\n\nor for a single provider:\n\n```ruby\nruby_runtime 'myapp' do\n version '2.1'\n options :system, dev_package: false\nend\n```\n\nSetting via node attributes is generally how an end-user or application cookbook\nwill set options to customize installations in the library cookbooks they are using.\nYou can set options for all installations or for a single runtime:\n\n```ruby\n# Global, for all installations.\noverride['poise-ruby']['options']['dev_package'] = false\n# Single installation.\noverride['poise-ruby']['myapp']['version'] = '2.2'\n```\n\nThe `ruby_runtime_options` resource is also available to set node attributes\nfor a specific installation in a DSL-friendly way:\n\n```ruby\nruby_runtime_options 'myapp' do\n version '2.2'\nend\n```\n\nUnlike resource attributes, provider options can be different for each provider.\nNot all providers support the same options so make sure to the check the\ndocumentation for each provider to see what options the use.\n\n### `ruby_runtime_options`\n\nThe `ruby_runtime_options` resource allows setting provider options in a\nDSL-friendly way. See [the Provider Options](#provider-options) section for more\ninformation about provider options overall.\n\n```ruby\nruby_runtime_options 'myapp' do\n version '2.2'\nend\n```\n\n#### Actions\n\n* `:run` – Apply the provider options. *(default)*\n\n#### Properties\n\n* `resource` – Name of the `ruby_runtime` resource. *(name property)*\n* `for_provider` – Provider to set options for.\n\nAll other property keys will be used as options data.\n\n### `ruby_execute`\n\nThe `ruby_execute` resource executes a Ruby script using the configured runtime.\n\n```ruby\nruby_execute 'myapp.rb' do\n user 'myuser'\nend\n```\n\nThis uses the built-in `execute` resource and supports all the same properties.\n\n#### Actions\n\n* `:run` – Execute the script. *(default)*\n\n#### Properties\n\n* `command` – Script and arguments to run. Must not include the `ruby`. *(name property)*\n* `ruby` – Name of the `ruby_runtime` resource to use. If not specified, the\n most recently declared `ruby_runtime` will be used.\n\nFor other properties see the [Chef documentation](https://docs.chef.io/resource_execute.html#attributes).\n\n### `ruby_gem`\n\nThe `ruby_gem` resource is a subclass of the standard `gem_package` resource to\ninstall the gem with the configured runtime.\n\n```ruby\nruby_gem 'rake' do\n version ' 10.4.2'\nend\n```\n\nAll actions and attributes match the standard `gem_package` resource with the\naddition of a `ruby` attribute matching `ruby_execute`.\n\n### `bundle_install`\n\nThe `bundle_install` resource installs gems based on a Gemfile using\n[bundler](http://bundler.io/).\n\n```ruby\nbundle_install '/path/to/Gemfile' do\n deployment true\n jobs 3\nend\n```\n\nThe underlying `bundle` command will run on every converge, but notifications\nwill only be triggered if a gem is actually installed.\n\n#### Actions\n\n* `:install` – Run `bundle install`. *(default)*\n* `:update` – Run `bundle update`.\n\n#### Properties\n\n* `path` – Path to a Gemfile or a directory containing a Gemfile. *(name property)*\n* `binstubs` – Enable binstubs. If set to a string it is the path to generate\n stubs in.\n* `bundler_version` – Version of bundler to install. If unset the latest version is used.\n* `deployment` – Enable deployment mode.\n* `gem_binary` – Path to the gem binary. If unset this uses the `ruby_runtime` parent.\n* `jobs` – Number of parallel installations to run.\n* `retry` – Number of times to retry failed installations.\n* `ruby` – Name of the `ruby_runtime` resource to execute against.\n* `user` – User to run bundler as.\n* `vendor` – Enable local vendoring. This maps to the `--path` option in bundler,\n but that attribute name is already used.\n* `without` – Group or groups to not install.\n\n## Ruby Providers\n\n### `system`\n\nThe `system` provider installs Ruby using system packages. This is currently\nonly tested on platforms using `apt-get` and `yum` (Debian, Ubuntu, RHEL, CentOS\nAmazon Linux, and Fedora) and is the default provider on those platforms. It\nmay work on other platforms but is untested.\n\n```ruby\nruby_runtime 'myapp' do\n provider :system\n version '2.1'\nend\n```\n\n#### Options\n\n* `dev_package` – Install the package with the headers and other development\n files. *(default: true)*\n* `rubygems_package` – Install rubygems from a package. This is only needed for\n Ruby 1.8. *(default: true on RHEL 6)*\n* `package_name` – Override auto-detection of the package name.\n* `package_upgrade` – Install using action `:upgrade`. *(default: false)*\n* `package_version` – Override auto-detection of the package version.\n* `version` – Override the Ruby version.\n\n### `scl`\n\nThe `scl` provider installs Ruby using the [Software Collections](https://www.softwarecollections.org/)\npackages. This is only available on RHEL and CentOS. SCL offers more\nrecent versions of Ruby than the system packages for the most part. If an SCL\npackage exists for the requested version, it will be used in preference to the\n`system` provider.\n\n```ruby\nruby_runtime 'myapp' do\n provider :scl\n version '2.2'\nend\n```\n\n### `chef`\n\nThe `chef` provider uses the Ruby environment included in the Omnibus packages.\nGreat care should be taken when using this provider.\n\n```ruby\nruby_runtime 'myapp' do\n provider :chef\n version '2.1'\nend\n```\n\n#### Options\n\n* `version` – Override the Ruby version.\n\n### `ruby_build`\n\nThe `ruby_build` provider uses [ruby-build](https://github.com/sstephenson/ruby-build)\nto compile and install Ruby. It can be found in the\n[poise-ruby-build cookbook](https://github.com/poise/poise-ruby-build).\n\n## Sponsors\n\nDevelopment sponsored by [Bloomberg](http://www.bloomberg.com/company/technology/).\n\nThe Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/).\n\n## License\n\nCopyright 2015-2016, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainer":"Noah Kantrowitz","maintainer_email":"noah@coderanger.net","license":"Apache 2.0","platforms":{},"dependencies":{"poise":"~> 2.0","poise-languages":"~> 2.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{},"source_url":"https://github.com/poise/poise-ruby","issues_url":"https://github.com/poise/poise-ruby/issues","chef_version":"~> 12","ohai_version":{}} \ No newline at end of file diff --git a/cookbooks/poise-ruby/recipes/default.rb b/cookbooks/poise-ruby/recipes/default.rb new file mode 100644 index 0000000..f5b8080 --- /dev/null +++ b/cookbooks/poise-ruby/recipes/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Default runtimes, last one will be the default. +ruby_runtime('chef') { provider :chef } if node['poise-ruby']['install_chef_ruby'] +ruby_runtime('ruby') { version '' } if node['poise-ruby']['install_ruby'] diff --git a/cookbooks/poise-service/CHANGELOG.md b/cookbooks/poise-service/CHANGELOG.md new file mode 100644 index 0000000..8364cfe --- /dev/null +++ b/cookbooks/poise-service/CHANGELOG.md @@ -0,0 +1,74 @@ +# Poise-Service Changelog + +## v1.4.2 + +* Fix the `noterm` test service to work on Ruby 2.3. + +## v1.4.1 + +* Fix `poise_service_user` on Solaris and make it closer to being usable on Windows. + +## v1.4.0 + +* [#31](https://github.com/poise/poise-service/pull/31) Add `shell` property to + `poise_service_user` resource. + +## v1.3.1 + +* [#25](https://github.com/poise/poise-service/pull/25) Cope with a service user + with an invalid home directory. +* Use the correct default cookbook for `service_template` when used with additional plugins. + +## v1.3.0 + +* Allow setting `pid_file_external false` as a provider option for the `sysvinit` + provider to have non-standard path but keep the internal handling. +* Improved quoting for environment variables in the `inittab` provider. + +## v1.2.1 + +* [#23](https://github.com/poise/poise-service/pull/23) Fix service templates on AIX and FreeBSD to use the correct root group. + +## v1.2.0 + +* The `Restart` mode for systemd services can now be controlled via provider + option and defaults to `on-failure` to match other providers. + +## v1.1.2 + +* [#22](https://github.com/poise/poise-service/pull/22) Set all script commands + for the `sysvinit` provider. This should fix compatibility with EL5. + +## v1.1.1 + +* Fix an incorrect value in `poise_service_test`. This is not relevant to + end-users of `poise-service`. + +## v1.1.0 + +* Added `inittab` provider to manage services using old-fashioned `/etc/inittab`. + +## v1.0.4 + +* Set GID correctly in all service providers. +* Allow overriding the path to the generated sysvinit script. + +## v1.0.3 + +* [#10](https://github.com/poise/poise-service/pull/10) Fixes for ensuring services are restarted when their command or user changes. +* [#11](https://github.com/poise/poise-service/pull/11) Revamp the `sysvinit` provider for non-Debian platforms to be more stable. +* [#12](https://github.com/poise/poise-service/pull/12) Improve the `dummy` provider to handle dropping privs correctly. + +## v1.0.2 + +* Fix a potential infinite loop when starting a service with the dummy provider. +* [#2](https://github.com/poise/poise-service/pull/2) Remove usage of root + default files so uploading with Berkshelf works (for now). + +## v1.0.1 + +* Don't use a shared, mutable default value for `#environment`. + +## v1.0.0 + +* Initial release! diff --git a/cookbooks/poise-service/README.md b/cookbooks/poise-service/README.md new file mode 100644 index 0000000..922b8dd --- /dev/null +++ b/cookbooks/poise-service/README.md @@ -0,0 +1,414 @@ +# Poise-Service Cookbook + +[![Build Status](https://img.shields.io/travis/poise/poise-service.svg)](https://travis-ci.org/poise/poise-service) +[![Gem Version](https://img.shields.io/gem/v/poise-service.svg)](https://rubygems.org/gems/poise-service) +[![Cookbook Version](https://img.shields.io/cookbook/v/poise-service.svg)](https://supermarket.chef.io/cookbooks/poise-service) +[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-service.svg)](https://codecov.io/github/poise/poise-service) +[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-service.svg)](https://gemnasium.com/poise/poise-service) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +A [Chef](https://www.chef.io/) cookbook to provide a unified interface for +services. + +### What is poise-service? + +Poise-service is a tool for developers of "library cookbooks" to define a +service without forcing the end-user of the library to adhere to their choice of +service management framework. The `poise_service` resource represents an +abstract service to be run, which can then be customized by node attributes and +the `poise_service_options` resource. This is a technique called [dependency +injection](https://en.wikipedia.org/wiki/Dependency_injection), and allows a +measure of decoupling between the library and application cookbooks. + +### Why would I use poise-service? + +Poise-service is most useful for authors of library-style cookbooks, for example +the `apache2`, `mysql`, or `application` cookbooks. When using other service +management options with Chef, the author of the library cookbook has to add +specific code for each service management framework they want to support, often +resulting in a cookbook only supporting the favorite framework of the author or +depending on distribution packages for their init scripts. The `poise_service` +resource allows library cookbook authors a way to write generic code for all +service management frameworks while still allowing users of that cookbook to +choose which service management framework best fits their needs. + +### How is this different from the built-in service resource? + +Chef includes a `service` resource which allows interacting with certain +service management frameworks such as SysV, Upstart, and systemd. +`poise-service` goes further in that it actually generates the configuration +files needed for the requested service management framework, as well as offering +a dependency injection system for application cookbooks to customize which +framework is used. + +### What service management frameworks are supported? + +* [SysV (aka /etc/init.d)](#sysvinit) +* [Upstart](#upstart) +* [systemd](#systemd) +* [Inittab](#inittab) +* [Runit](https://github.com/poise/poise-service-runit) +* [Monit](https://github.com/poise/poise-monit#service-provider) +* [Solaris](https://github.com/sh9189/poise-service-solaris) +* [AIX](https://github.com/johnbellone/poise-service-aix) +* *Supervisor (coming soon!)* + + +## Quick Start + +To create a service user and a service to run Apache2: + +```ruby +poise_service_user 'www-data' + +poise_service 'apache2' do + command '/usr/sbin/apache2 -f /etc/apache2/apache2.conf -DFOREGROUND' + stop_signal 'WINCH' + reload_signal 'USR1' +end +``` + +or for a hypothetical Rails web application: + +```ruby +poise_service_user 'myapp' + +poise_service 'myapp-web' do + command 'bundle exec unicorn -p 8080' + user 'myapp' + directory '/srv/myapp' + environment RAILS_ENV: 'production' +end +``` + +## Resources + +### `poise_service` + +The `poise_service` resource is the abstract definition of a service. + +```ruby +poise_service 'myapp' do + command 'myapp --serve' + environment RAILS_ENV: 'production' +end +``` + +#### Actions + +* `:enable` – Create, enable and start the service. *(default)* +* `:disable` – Stop, disable, and destroy the service. +* `:start` – Start the service. +* `:stop` – Stop the service. +* `:restart` – Stop and then start the service. +* `:reload` – Send the configured reload signal to the service. + +#### Attributes + +* `service_name` – Name of the service. *(name attribute)* +* `command` – Command to run for the service. This command must stay in the + foreground and not daemonize itself. *(required)* +* `user` – User to run the service as. See + [`poise_service_user`](#poise_service_user) for any easy way to create service + users. *(default: root)* +* `directory` – Working directory for the service. *(default: home directory for + user, or / if not found)* +* `environment` – Environment variables for the service. +* `stop_signal` – Signal to use to stop the service. Some systems will fall back + to SIGKILL if this signal fails to stop the process. *(default: TERM)* +* `reload_signal` – Signal to use to reload the service. *(default: HUP)* +* `restart_on_update` – If true, the service will be restarted if the service + definition or configuration changes. If `'immediately'`, the notification will + happen in immediate mode. *(default: true)* + +#### Service Options + +The `poise-service` library offers an additional way to pass configuration +information to the final service called "options". Options are key/value pairs +that are passed down to the service provider and can be used to control how it +creates and manages the service. These can be set in the `poise_service` +resource using the `options` method, in node attributes or via the +`poise_service_options` resource. The options from all sources are merged +together in to a single hash. + +When setting options in the resource you can either set them for all providers: + +```ruby +poise_service 'myapp' do + command 'myapp --serve' + options status_port: 8000 +end +``` + +or for a single provider: + +```ruby +poise_service 'myapp' do + command 'myapp --serve' + options :systemd, after_target: 'network' +end +``` + +Setting via node attributes is generally how an end-user or application cookbook +will set options to customize services in the library cookbooks they are using. +You can set options for all services or for a single service, by service name +or by resource name: + +```ruby +# Global, for all services. +override['poise-service']['options']['after_target'] = 'network' +# Single service. +override['poise-service']['myapp']['template'] = 'myapp.erb' +``` + +The `poise_service_options` resource is also available to set node attributes +for a specific service in a DSL-friendly way: + +```ruby +poise_service_options 'myapp' do + template 'myapp.erb' + restart_on_update false +end +``` + +Unlike resource attributes, service options can be different for each provider. +Not all providers support the same options so make sure to check the +documentation for each provider to see what options are available. + +### `poise_service_options` + +The `poise_service_options` resource allows setting per-service options in a +DSL-friendly way. See [the Service Options](#service-options) section for more +information about service options overall. + +```ruby +poise_service_options 'myapp' do + template 'myapp.erb' + restart_on_update false +end +``` + +#### Actions + +* `:run` – Apply the service options. *(default)* + +#### Attributes + +* `resource` – Name of the service. *(name attribute)* +* `for_provider` – Provider to set options for. + +All other attribute keys will be used as options data. + +### `poise_service_user` + +The `poise_service_user` resource is an easy way to create service users. It is +not required to use `poise_service`, it is only a helper. + +```ruby +poise_service_user 'myapp' do + home '/srv/myapp' +end +``` + +#### Actions + +* `:create` – Create the user and group. *(default)* +* `:remove` – Remove the user and group. + +#### Attributes + +* `user` – Name of the user. *(name attribute)* +* `group` – Name of the group. Set to `false` to disable group creation. *(name attribute)* +* `uid` – UID of the user. *(default: automatic)* +* `gid` – GID of the group. *(default: automatic)* +* `home` – Home directory of the user. +* `shell` – Shell of the user. *(default: /bin/nologin if present or /bin/false)* + +## Providers + +### `sysvinit` + +The `sysvinit` provider supports SystemV-style init systems on Debian-family and +RHEL-family platforms. It will create the `/etc/init.d/` script +and enable/disable the service using the platform-specific service resource. + +```ruby +poise_service 'myapp' do + provider :sysvinit + command 'myapp --serve' +end +``` + +By default a PID file will be created in `/var/run/service_name.pid`. You can +use the `pid_file` option detailed below to override this and rely on your +process creating a PID file in the given path. + +#### Options + +* `pid_file` – Path to PID file that the service command will create. +* `pid_file_external` – If true, assume the service will create the PID file + itself. *(default: true if `pid_file` option is set)* +* `template` – Override the default script template. If you want to use a + template in a different cookbook use `'cookbook:template'`. +* `command` – Override the service command. +* `directory` – Override the service directory. +* `environment` – Override the service environment variables. +* `reload_signal` – Override the service reload signal. +* `stop_signal` – Override the service stop signal. +* `user` – Override the service user. +* `never_restart` – Never try to restart the service. +* `never_reload` – Never try to reload the service. +* `script_path` – Override the path to the generated service script. + +### `upstart` + +The `upstart` provider supports [Upstart](http://upstart.ubuntu.com/). It will +create the `/etc/init/service_name.conf` configuration. + +```ruby +poise_service 'myapp' do + provider :upstart + command 'myapp --serve' +end +``` + +As a wide variety of versions of Upstart are in use in various Linux +distributions, the provider does its best to identify which features are +available and provide shims as appropriate. Most of these should be invisible +however Upstart older than 1.10 does not support setting a `reload signal` so +only SIGHUP can be used. You can set a `reload_shim` option to enable an +internal implementaion of reloading to be used for signals other than SIGHUP, +however as this is implemented inside Chef code, running `initctl reload` would +still result in SIGHUP being sent. For this reason, the feature is disabled by +default and will throw an error if a reload signal other than SIGHUP is used. + +#### Options + +* `reload_shim` – Enable the reload signal shim. See above for a warning about + this feature. +* `template` – Override the default configuration template. If you want to use a + template in a different cookbook use `'cookbook:template'`. +* `command` – Override the service command. +* `directory` – Override the service directory. +* `environment` – Override the service environment variables. +* `reload_signal` – Override the service reload signal. +* `stop_signal` – Override the service stop signal. +* `user` – Override the service user. +* `never_restart` – Never try to restart the service. +* `never_reload` – Never try to reload the service. + +### `systemd` + +The `systemd` provider supports [systemd](http://www.freedesktop.org/wiki/Software/systemd/). +It will create the `/etc/systemd/system/service_name.service` configuration. + + +```ruby +poise_service 'myapp' do + provider :systemd + command 'myapp --serve' +end +``` + +#### Options + +* `template` – Override the default configuration template. If you want to use a + template in a different cookbook use `'cookbook:template'`. +* `command` – Override the service command. +* `directory` – Override the service directory. +* `environment` – Override the service environment variables. +* `reload_signal` – Override the service reload signal. +* `stop_signal` – Override the service stop signal. +* `user` – Override the service user. +* `never_restart` – Never try to restart the service. +* `never_reload` – Never try to reload the service. +* `auto_reload` – Run `systemctl daemon-reload` after changes to the unit file. *(default: true)* +* `restart_mode` – Restart mode for the generated service unit. *(default: on-failure)* + +### `inittab` + +The `inittab` provider supports managing services via `/etc/inittab` using +[SystemV Init](http://www.nongnu.org/sysvinit/). This can provide basic +process supervision even on very old *nix machines. + +```ruby +poise_service 'myapp' do + provider :inittab + command 'myapp --serve' +end +``` + +**NOTE:** Inittab does not allow stopping services, and they are started as soon +as they are enabled. + +#### Options + +* `never_restart` – Never try to restart the service. +* `never_reload` – Never try to reload the service. +* `pid_file` – Path to PID file that the service command will create. +* `service_id` – Unique 1-4 character tag for the service. Defaults to an + auto-generated hash based on the service name. If these collide, bad things + happen. Don't do that. + +## ServiceMixin + +For the common case of a resource (LWRP or plain Ruby) that roughly maps to +"some config files and a service" poise-service provides a mixin module, +`PoiseService::ServiceMixin`. This mixin adds the standard service actions +(`enable`, `disable`, `start`, `stop`, `restart`, and `reload`) with basic +implementations that call those actions on a `poise_service` resource for you. +You customize the service by defining a `service_options` method on your +provider class: + +```ruby +def service_options(service) + # service is the PoiseService::Resource object instance. + service.command "/usr/sbin/#{new_resource.name} -f /etc/#{new_resource.name}/conf/httpd.conf -DFOREGROUND" + service.stop_signal 'WINCH' + service.reload_signal 'USR1' +end +``` + +You will generally want to override the `enable` action to install things +related to the service like packages, users and configuration files: + +```ruby +def action_enable + notifying_block do + package 'apache2' + poise_service_user 'www-data' + template "/etc/#{new_resource.name}/conf/httpd.conf" do + # ... + end + end + # This super call will run the normal service enable, + # creating the service and starting it. + super +end +``` + +See [the poise_service_test_mixin resource](test/cookbooks/poise-service_test/resources/mixin.rb) +and [provider](test/cookbooks/poise-service_test/providers/mixin.rb) for +examples of using `ServiceMixin` in an LWRP. + +## Sponsors + +Development sponsored by [Bloomberg](http://www.bloomberg.com/company/technology/). + +The Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/). + +## License + +Copyright 2015-2016, Noah Kantrowitz + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cookbooks/poise-service/attributes/default.rb b/cookbooks/poise-service/attributes/default.rb new file mode 100644 index 0000000..2e8490d --- /dev/null +++ b/cookbooks/poise-service/attributes/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['poise-service']['provider'] = 'auto' + +default['poise-service']['options'] = {} diff --git a/cookbooks/poise-service/files/halite_gem/poise_service.rb b/cookbooks/poise-service/files/halite_gem/poise_service.rb new file mode 100644 index 0000000..ad2ec60 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service.rb @@ -0,0 +1,25 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseService + autoload :Error, 'poise_service/error' + autoload :Resources, 'poise_service/resources' + autoload :ServiceMixin, 'poise_service/service_mixin' + autoload :ServiceProviders, 'poise_service/service_providers' + autoload :Utils, 'poise_service/utils' + autoload :VERSION, 'poise_service/version' +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/cheftie.rb b/cookbooks/poise-service/files/halite_gem/poise_service/cheftie.rb new file mode 100644 index 0000000..31d59db --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/cheftie.rb @@ -0,0 +1,18 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_service/resources' +require 'poise_service/service_providers' diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/error.rb b/cookbooks/poise-service/files/halite_gem/poise_service/error.rb new file mode 100644 index 0000000..5029795 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/error.rb @@ -0,0 +1,20 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module PoiseService + class Error < ::Exception + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/resources.rb b/cookbooks/poise-service/files/halite_gem/poise_service/resources.rb new file mode 100644 index 0000000..b0e1ddb --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/resources.rb @@ -0,0 +1,27 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_service/resources/poise_service' +require 'poise_service/resources/poise_service_user' + + +module PoiseService + # Chef resources and providers for poise-service. + # + # @since 1.0.0 + module Resources + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/resources/poise_service.rb b/cookbooks/poise-service/files/halite_gem/poise_service/resources/poise_service.rb new file mode 100644 index 0000000..157a584 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/resources/poise_service.rb @@ -0,0 +1,165 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'etc' + +require 'chef/mash' +require 'chef/resource' +require 'poise' + +require 'poise_service/error' + + +module PoiseService + module Resources + # (see PoiseService::Resource) + module PoiseService + # `poise_service` resource. Provides a unified service interface with a + # dependency injection framework. + # + # @since 1.0.0 + # @provides poise_service + # @action enable + # @action disable + # @action start + # @action stop + # @action restart + # @action reload + # @example + # poise_service 'myapp' do + # command 'myapp --serve' + # user 'myuser' + # directory '/home/myapp' + # end + class Resource < Chef::Resource + include Poise(inversion: true) + provides(:poise_service) + actions(:enable, :disable, :start, :stop, :restart, :reload) + + # @!attribute service_name + # Name of the service to the underlying init system. Defaults to the name + # of the resource. + # @return [String] + attribute(:service_name, kind_of: String, name_attribute: true) + # @!attribute command + # Command to run inside the service. This command must remain in the + # foreground and not daemoinize itself. + # @return [String] + attribute(:command, kind_of: String, required: true) + # @!attribute user + # User to run the service as. See {UserResource} for an easy way to + # create service users. Defaults to root. + # @return [String] + attribute(:user, kind_of: String, default: 'root') + # @!attribute directory + # Working directory for the service. Defaults to the home directory of + # the configured user or / if not found. + # @return [String] + attribute(:directory, kind_of: String, default: lazy { default_directory }) + # @!attribute environment + # Environment variables for the service. + # @return [Hash] + attribute(:environment, kind_of: Hash, default: lazy { Mash.new }) + # @!attribute stop_signal + # Signal to use to stop the service. Some systems will fall back to + # KILL if this signal fails to stop the process. Defaults to TERM. + # @return [String, Symbol, Integer] + attribute(:stop_signal, kind_of: [String, Symbol, Integer], default: 'TERM') + # @!attribute reload_signal + # Signal to use to reload the service. Defaults to HUP. + # @return [String, Symbol, Integer] + attribute(:reload_signal, kind_of: [String, Symbol, Integer], default: 'HUP') + # @!attribute restart_on_update + # If true, the service will be restarted if the service definition or + # configuration changes. If 'immediately', the notification will happen + # in immediate mode. + # @return [Boolean, String] + attribute(:restart_on_update, equal_to: [true, false, 'immediately', :immediately], default: true) + + # Resource DSL callback. + # + # @api private + def after_created + # Set signals to clean values. + stop_signal(clean_signal(stop_signal)) + reload_signal(clean_signal(reload_signal)) + end + + # Return the PID of the main process for this service or nil if the service + # isn't running or the PID cannot be found. + # + # @return [Integer, nil] + # @example + # execute "kill -WINCH #{resources('poise_test[myapp]').pid}" + def pid + # :pid isn't a real action, but this should still work. + provider_for_action(:pid).pid + end + + private + + # Try to find the home diretory for the configured user. This will fail if + # nsswitch.conf was changed during this run such as with LDAP. Defaults to + # the system root directory. + # + # @see #directory + # @return [String] + def default_directory + # Default fallback. + sysroot = case node['platform_family'] + when 'windows' + ENV.fetch('SystemRoot', 'C:\\') + else + '/' + end + # For root we always want the system root path. + return sysroot if user == 'root' + # Force a reload in case any users were created earlier in the run. + Etc.endpwent + # ArgumentError means we can't find the user, possibly nsswitch caching? + home = begin + Dir.home(user) + rescue ArgumentError + sysroot + end + # If the home doesn't exist or is empty, use sysroot. + home = sysroot if home.empty? || !::File.directory?(home) + home + end + + # Clean up a signal string/integer. Ints are mapped to the signal name, + # and strings are reformatted to upper case and without the SIG. + # + # @see #stop_signal + # @param signal [String, Symbol, Integer] Signal value to clean. + # @return [String] + def clean_signal(signal) + if signal.is_a?(Integer) + raise Error.new("Unknown signal #{signal}") unless (0..31).include?(signal) + Signal.signame(signal) + else + short_sig = signal.to_s.upcase + short_sig = short_sig[3..-1] if short_sig.start_with?('SIG') + raise Error.new("Unknown signal #{signal}") unless Signal.list.include?(short_sig) + short_sig + end + end + + # Providers can be found under service_providers/. + end + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/resources/poise_service_test.rb b/cookbooks/poise-service/files/halite_gem/poise_service/resources/poise_service_test.rb new file mode 100644 index 0000000..393ae21 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/resources/poise_service_test.rb @@ -0,0 +1,240 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'chef/provider' +require 'poise' + + +module PoiseService + module Resources + # (see PoiseServiceTest::Resource) + module PoiseServiceTest + # A `poise_service_test` resource for integration testing service providers. + # This is used in Test-Kitchen tests to ensure all providers behave + # similarly. + # + # @since 1.0.0 + # @provides poise_service_test + # @action run + # @example + # poise_service_test 'upstart' do + # service_provider :upstart + # base_port 5000 + # end + class Resource < Chef::Resource + include Poise + provides(:poise_service_test) + actions(:run) + + # @!attribute service_provider + # Service provider to set for the test group. + # @return [Symbol] + attribute(:service_provider, kind_of: Symbol) + # @!attribute service_options + # Service options to set for the test group. + # @return [Hash, nil] + attribute(:service_options, kind_of: [Hash, NilClass]) + # @!attribute base_port + # Port number to start from for the test group. + # @return [Integer] + attribute(:base_port, kind_of: Integer) + end + + # Provider for `poise_service_test`. + # + # @see Resource + # @provides poise_service_test + class Provider < Chef::Provider + include Poise + provides(:poise_service_test) + + SERVICE_SCRIPT = <<-EOH +require 'webrick' +require 'json' +require 'etc' +FILE_DATA = '' +server = WEBrick::HTTPServer.new(Port: ARGV[0].to_i) +server.mount_proc '/' do |req, res| + res.body = { + directory: Dir.getwd, + user: Etc.getpwuid(Process.uid).name, + euser: Etc.getpwuid(Process.euid).name, + group: Etc.getgrgid(Process.gid).name, + egroup: Etc.getgrgid(Process.egid).name, + environment: ENV.to_hash, + file_data: FILE_DATA, + pid: Process.pid, + }.to_json +end +EOH + + # `run` action for `poise_service_test`. Create all test services. + # + # @return [void] + def action_run + notifying_block do + create_script + create_noterm_script + create_user + create_tests + end + end + + private + + def create_script + file '/usr/bin/poise_test' do + owner 'root' + group 'root' + mode '755' + content <<-EOH +#!/opt/chef/embedded/bin/ruby +#{SERVICE_SCRIPT} +def load_file + FILE_DATA.replace(IO.read(ARGV[1])) +end +if ARGV[1] + load_file + trap('HUP') do + load_file + end +end +server.start +EOH + end + end + + def create_noterm_script + file '/usr/bin/poise_test_noterm' do + owner 'root' + group 'root' + mode '755' + content <<-EOH +#!/opt/chef/embedded/bin/ruby +trap('HUP', 'IGNORE') +trap('TERM', 'IGNORE') +#{SERVICE_SCRIPT} +while true + begin + server.start + rescue Exception + rescue StandardError + end +end +EOH + end + end + + def create_user + poise_service_user 'poise' do + home '/tmp' + end + end + + def create_tests + poise_service "poise_test_#{new_resource.name}" do + if new_resource.service_provider + provider new_resource.service_provider + options new_resource.service_provider, new_resource.service_options if new_resource.service_options + end + command "/usr/bin/poise_test #{new_resource.base_port}" + end + + poise_service "poise_test_#{new_resource.name}_params" do + if new_resource.service_provider + provider new_resource.service_provider + options new_resource.service_provider, new_resource.service_options if new_resource.service_options + end + command "/usr/bin/poise_test #{new_resource.base_port + 1}" + environment POISE_ENV: new_resource.name + user 'poise' + end + + poise_service "poise_test_#{new_resource.name}_noterm" do + if new_resource.service_provider + provider new_resource.service_provider + options new_resource.service_provider, new_resource.service_options if new_resource.service_options + end + action [:enable, :disable] + command "/usr/bin/poise_test_noterm #{new_resource.base_port + 2}" + stop_signal 'kill' + end + + {'restart' => 3, 'reload' => 4}.each do |action, port| + # Stop it before writing the file so we always start with first. + poise_service "poise_test_#{new_resource.name}_#{action} stop" do + if new_resource.service_provider + provider new_resource.service_provider + options new_resource.service_provider, new_resource.service_options if new_resource.service_options + end + action(:disable) + service_name "poise_test_#{new_resource.name}_#{action}" + end + + # Write the content to the read on service launch. + file "/etc/poise_test_#{new_resource.name}_#{action}" do + content 'first' + end + + # Launch the service, reading in first. + poise_service "poise_test_#{new_resource.name}_#{action}" do + if new_resource.service_provider + provider new_resource.service_provider + options new_resource.service_provider, new_resource.service_options if new_resource.service_options + end + command "/usr/bin/poise_test #{new_resource.base_port + port} /etc/poise_test_#{new_resource.name}_#{action}" + end + + # Rewrite the file to second, restart/reload to trigger an update. + file "/etc/poise_test_#{new_resource.name}_#{action} again" do + path "/etc/poise_test_#{new_resource.name}_#{action}" + content 'second' + notifies action.to_sym, "poise_service[poise_test_#{new_resource.name}_#{action}]" + end + end + + # Test the #pid accessor. + ruby_block "/tmp/poise_test_#{new_resource.name}_pid" do + block do + pid = resources("poise_service[poise_test_#{new_resource.name}]").pid + IO.write("/tmp/poise_test_#{new_resource.name}_pid", pid.to_s) + end + end + + # Test changing the service definition itself. + poise_service "poise_test_#{new_resource.name}_change" do + if new_resource.service_provider + provider new_resource.service_provider + options new_resource.service_provider, new_resource.service_options if new_resource.service_options + end + command "/usr/bin/poise_test #{new_resource.base_port + 5}" + end + + poise_service "poise_test_#{new_resource.name}_change_second" do + service_name "poise_test_#{new_resource.name}_change" + if new_resource.service_provider + provider new_resource.service_provider + options new_resource.service_provider, new_resource.service_options if new_resource.service_options + end + command "/usr/bin/poise_test #{new_resource.base_port + 6}" + end + + end + end + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/resources/poise_service_user.rb b/cookbooks/poise-service/files/halite_gem/poise_service/resources/poise_service_user.rb new file mode 100644 index 0000000..6d4eac4 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/resources/poise_service_user.rb @@ -0,0 +1,186 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' +require 'chef/provider' +require 'poise' + + +module PoiseService + module Resources + # (see PoiseServiceUser::Resource) + # @since 1.0.0 + module PoiseServiceUser + # Shells to look for in order. + # @api private + DEFAULT_SHELLS = %w{/bin/nologin /usr/bin/nologin /bin/false} + + # A `poise_service_user` resource to create service users/groups. + # + # @since 1.0.0 + # @provides poise_service_user + # @action create + # @action remove + # @example + # poise_service_user 'myapp' do + # home '/var/tmp' + # group 'nogroup' + # end + class Resource < Chef::Resource + include Poise + provides(:poise_service_user) + actions(:create, :remove) + + # @!attribute user + # Name of the user to create. Defaults to the name of the resource. + # @return [String] + attribute(:user, kind_of: String, name_attribute: true) + # @!attribute group + # Name of the group to create. Defaults to the name of the user, + # except on Windows where it defaults to false. Set to false to + # disable group creation. + # @return [String, false] + attribute(:group, kind_of: [String, FalseClass], default: lazy { default_group }) + # @!attribute uid + # UID of the user to create. Optional, if not set the UID will be + # allocated automatically. + # @return [Integer] + attribute(:uid, kind_of: Integer) + # @!attribute gid + # GID of the group to create. Optional, if not set the GID will be + # allocated automatically. + # @return [Integer] + attribute(:gid, kind_of: Integer) + # @!attribute shell + # Login shell for the user. Optional, if not set the shell will be + # determined automatically. + # @return [String] + attribute(:shell, kind_of: String, default: lazy { default_shell }) + # @!attribute home + # Home directory of the user. This directory will not be created if it + # does not exist. Optional. + # @return [String] + attribute(:home, kind_of: String) + + private + + # Find a default shell for service users. Tries to use nologin, but fall + # back on false. + # + # @api private + # @return [String] + def default_shell + DEFAULT_SHELLS.find {|s| ::File.exist?(s) } || DEFAULT_SHELLS.last + end + + # Find the default group name. Returns false on Windows because service + # groups aren't needed there. Otherwise use the name of the service user. + # + # @api private + # @return [String, false] + def default_group + if node.platform_family?('windows') + false + else + user + end + end + end + + # Provider for `poise_service_user`. + # + # @since 1.0.0 + # @see Resource + # @provides poise_service_user + class Provider < Chef::Provider + include Poise + provides(:poise_service_user) + + # `create` action for `poise_service_user`. Ensure the user and group (if + # enabled) exist. + # + # @return [void] + def action_create + notifying_block do + create_group if new_resource.group + create_user + end + end + + # `remove` action for `poise_service_user`. Ensure the user and group (if + # enabled) are destroyed. + # + # @return [void] + def action_remove + notifying_block do + remove_user + remove_group if new_resource.group + end + end + + private + + # Create the system group. + # + # @api private + # @return [void] + def create_group + group new_resource.group do + gid new_resource.gid + # Solaris doesn't support the idea of system groups. + system true unless node.platform_family?('solaris2') + end + end + + # Create the system user. + # + # @api private + # @return [void] + def create_user + user new_resource.user do + comment "Service user for #{new_resource.name}" + gid new_resource.group if new_resource.group + home new_resource.home + shell new_resource.shell + # Solaris doesn't support the idea of system users. + system true unless node.platform_family?('solaris2') + uid new_resource.uid + end + end + + # Remove the system group. + # + # @api private + # @return [void] + def remove_group + create_group.tap do |r| + r.action(:remove) + end + end + + # Remove the system user. + # + # @api private + # @return [void] + def remove_user + create_user.tap do |r| + r.action(:remove) + end + end + end + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/service_mixin.rb b/cookbooks/poise-service/files/halite_gem/poise_service/service_mixin.rb new file mode 100644 index 0000000..da88a86 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/service_mixin.rb @@ -0,0 +1,192 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise' + +require 'poise_service/resources/poise_service' + + +module PoiseService + # Mixin for application services. This is any resource that will be part of + # an application deployment and involves running a persistent service. + # + # @since 1.0.0 + # @example + # module MyApp + # class Resource < Chef::Resource + # include Poise + # provides(:my_app) + # include PoiseService::ServiceMixin + # end + # + # class Provider < Chef::Provider + # include Poise + # provides(:my_app) + # include PoiseService::ServiceMixin + # + # def action_enable + # notifying_block do + # template '/etc/myapp.conf' do + # # ... + # end + # end + # super + # end + # + # def service_options(r) + # r.command('myapp --serve') + # end + # end + # end + module ServiceMixin + include Poise::Utils::ResourceProviderMixin + + # Mixin for service wrapper resources. + # + # @see ServiceMixin + module Resource + include Poise::Resource + + module ClassMethods + # @api private + def included(klass) + super + klass.extend(ClassMethods) + klass.class_exec do + actions(:enable, :disable, :start, :stop, :restart, :reload) + attribute(:service_name, kind_of: String, name_attribute: true) + end + end + end + + extend ClassMethods + end + + # Mixin for service wrapper providers. + # + # @see ServiceMixin + module Provider + include Poise::Provider + + # Default enable action for service wrappers. + # + # @return [void] + def action_enable + notify_if_service do + service_resource.run_action(:enable) + end + end + + # Default disable action for service wrappers. + # + # @return [void] + def action_disable + notify_if_service do + service_resource.run_action(:disable) + end + end + + # Default start action for service wrappers. + # + # @return [void] + def action_start + notify_if_service do + service_resource.run_action(:start) + end + end + + # Default stop action for service wrappers. + # + # @return [void] + def action_stop + notify_if_service do + service_resource.run_action(:stop) + end + end + + # Default restart action for service wrappers. + # + # @return [void] + def action_restart + notify_if_service do + service_resource.run_action(:restart) + end + end + + # Default reload action for service wrappers. + # + # @return [void] + def action_reload + notify_if_service do + service_resource.run_action(:reload) + end + end + + # @todo Add reload once poise-service supports it. + + private + + # Set the current resource as notified if the provided block updates the + # service resource. + # + # @api public + # @param block [Proc] Block to run. + # @return [void] + # @example + # notify_if_service do + # service_resource.run_action(:enable) + # end + def notify_if_service(&block) + service_resource.updated_by_last_action(false) + block.call if block + new_resource.updated_by_last_action(true) if service_resource.updated_by_last_action? + end + + # Service resource for this service wrapper. This returns a + # poise_service resource that will not be added to the resource + # collection. Override {#service_options} to set service resource + # parameters. + # + # @api public + # @return [Chef::Resource] + # @example + # service_resource.run_action(:restart) + def service_resource + @service_resource ||= PoiseService::Resources::PoiseService::Resource.new(new_resource.name, run_context).tap do |r| + # Set some defaults. + r.enclosing_provider = self + r.source_line = new_resource.source_line + r.service_name(new_resource.service_name) + # Call the subclass hook for more specific settings. + service_options(r) + end + end + + # Abstract hook to set parameters on {#service_resource} when it is + # created. This is required to set at least `resource.command`. + # + # @api public + # @param resource [Chef::Resource] Resource instance to set parameters on. + # @return [void] + # @example + # def service_options(resource) + # resource.command('myapp --serve') + # end + def service_options(resource) + end + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/service_providers.rb b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers.rb new file mode 100644 index 0000000..6cee1ae --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers.rb @@ -0,0 +1,38 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/platform/provider_priority_map' + +require 'poise_service/service_providers/dummy' +require 'poise_service/service_providers/inittab' +require 'poise_service/service_providers/systemd' +require 'poise_service/service_providers/sysvinit' +require 'poise_service/service_providers/upstart' + + +module PoiseService + # Inversion providers for the poise_service resource. + # + # @since 1.0.0 + module ServiceProviders + # Set up priority maps + Chef::Platform::ProviderPriorityMap.instance.priority(:poise_service, [ + PoiseService::ServiceProviders::Systemd, + PoiseService::ServiceProviders::Upstart, + PoiseService::ServiceProviders::Sysvinit, + ]) + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/base.rb b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/base.rb new file mode 100644 index 0000000..f5e2408 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/base.rb @@ -0,0 +1,193 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'poise' + + +module PoiseService + module ServiceProviders + class Base < Chef::Provider + include Poise(inversion: :poise_service) + + # Extend the default lookup behavior to check for service_name too. + # + # @api private + def self.resolve_inversion_provider(node, resource) + attrs = resolve_inversion_attribute(node) + (attrs[resource.service_name] && attrs[resource.service_name]['provider']) || super + end + + # Extend the default options to check for service_name too. + # + # @api private + def self.inversion_options(node, resource) + super.tap do |opts| + attrs = resolve_inversion_attribute(node) + opts.update(attrs[resource.service_name]) if attrs[resource.service_name] + run_state = Mash.new(node.run_state.fetch('poise_inversion', {}).fetch(inversion_resource, {}))[resource.service_name] || {} + opts.update(run_state['*']) if run_state['*'] + opts.update(run_state[provides]) if run_state[provides] + end + end + + # Cache the service hints to improve performance. This is called from the + # provides_auto? on most service providers and hits the filesystem a lot. + # + # @return [Array] + def self.service_resource_hints + @@service_resource_hints ||= Chef::Platform::ServiceHelpers.service_resource_providers + end + + def action_enable + include_recipe(*Array(recipes)) if recipes + notifying_block do + create_service + end + enable_service + action_start + end + + def action_disable + action_stop + disable_service + notifying_block do + destroy_service + end + end + + def action_start + notify_if_service do + service_resource.run_action(:start) + end + end + + def action_stop + notify_if_service do + service_resource.run_action(:stop) + end + end + + def action_restart + return if options['never_restart'] + notify_if_service do + service_resource.run_action(:restart) + end + end + + def action_reload + return if options['never_reload'] + notify_if_service do + service_resource.run_action(:reload) + end + end + + def pid + raise NotImplementedError + end + + private + + # Recipes to include for this provider to work. Subclasses can override. + # + # @return [String, Array] + def recipes + end + + # Subclass hook to create the required files et al for the service. + def create_service + raise NotImplementedError + end + + # Subclass hook to remove the required files et al for the service. + def destroy_service + raise NotImplementedError + end + + def enable_service + notify_if_service do + service_resource.run_action(:enable) + end + end + + def disable_service + notify_if_service do + service_resource.run_action(:disable) + end + end + + def notify_if_service(&block) + service_resource.updated_by_last_action(false) + block.call + new_resource.updated_by_last_action(true) if service_resource.updated_by_last_action? + end + + # Subclass hook to create the resource used to delegate start, stop, and + # restart actions. + def service_resource + @service_resource ||= Chef::Resource::Service.new(new_resource.service_name, run_context).tap do |r| + r.enclosing_provider = self + r.source_line = new_resource.source_line + r.supports(status: true, restart: true, reload: true) + end + end + + def service_template(path, default_source, &block) + # Sigh scoping. + template path do + owner 'root' + group node['root_group'] + mode '644' + if options['template'] + # If we have a template override, allow specifying a cookbook via + # "cookbook:template". + parts = options['template'].split(/:/, 2) + if parts.length == 2 + source parts[1] + cookbook parts[0] + else + source parts.first + cookbook new_resource.cookbook_name.to_s + end + else + source default_source + cookbook self.poise_defined_in_cookbook + end + variables( + command: options['command'] || new_resource.command, + directory: options['directory'] || new_resource.directory, + environment: options['environment'] || new_resource.environment, + name: new_resource.service_name, + new_resource: new_resource, + options: options, + reload_signal: options['reload_signal'] || new_resource.reload_signal, + stop_signal: options['stop_signal'] || new_resource.stop_signal, + user: options['user'] || new_resource.user, + ) + # Don't trigger a restart if the template doesn't already exist, this + # prevents restarting on the run that first creates the service. + restart_on_update = options.fetch('restart_on_update', new_resource.restart_on_update) + if restart_on_update && ::File.exist?(path) + mode = restart_on_update.to_s == 'immediately' ? :immediately : :delayed + notifies :restart, new_resource, mode + end + instance_exec(&block) if block + end + end + + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/dummy.rb b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/dummy.rb new file mode 100644 index 0000000..7cc6378 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/dummy.rb @@ -0,0 +1,156 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'etc' +require 'shellwords' + +require 'poise_service/service_providers/base' + + +module PoiseService + module ServiceProviders + class Dummy < Base + provides(:dummy) + + def action_start + return if pid + Chef::Log.debug("[#{new_resource}] Starting #{new_resource.command}") + # Clear the pid file if it exists. + ::File.unlink(pid_file) if ::File.exist?(pid_file) + if Process.fork + # Parent, wait for the final child to write the pid file. + now = Time.now + until ::File.exist?(pid_file) + sleep(1) + # After 30 seconds, show output at a higher level to avoid too much + # confusing on failed process launches. + if Time.now - now <= 30 + Chef::Log.debug("[#{new_resource}] Waiting for PID file") + else + Chef::Log.warning("[#{new_resource}] Waiting for PID file at #{pid_file} to be created") + end + end + else + # :nocov: + Chef::Log.debug("[#{new_resource}] Forked") + # First child, daemonize and go to town. This handles multi-fork, + # setsid, and shutting down stdin/out/err. + Process.daemon(true) + Chef::Log.debug("[#{new_resource}] Daemonized") + # Daemonized, set up process environment. + Dir.chdir(new_resource.directory) + Chef::Log.debug("[#{new_resource}] Directory changed to #{new_resource.directory}") + ENV['HOME'] = Dir.home(new_resource.user) + new_resource.environment.each do |key, val| + ENV[key.to_s] = val.to_s + end + Chef::Log.debug("[#{new_resource}] Process environment configured") + IO.write(pid_file, Process.pid) + Chef::Log.debug("[#{new_resource}] PID written to #{pid_file}") + ent = Etc.getpwnam(new_resource.user) + if Process.euid != ent.uid || Process.egid != ent.gid + Process.initgroups(ent.name, ent.gid) + Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid + Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid + end + Chef::Log.debug("[#{new_resource}] Changed privs to #{new_resource.user} (#{ent.uid}:#{ent.gid})") + # Split the command so we don't get an extra sh -c. + Chef::Log.debug("[#{new_resource}] Execing #{new_resource.command}") + Kernel.exec(*Shellwords.split(new_resource.command)) + # Just in case, bail out. + exit! + # :nocov: + end + Chef::Log.debug("[#{new_resource}] Started.") + end + + def action_stop + return unless pid + Chef::Log.debug("[#{new_resource}] Stopping with #{new_resource.stop_signal}. Current PID is #{pid.inspect}.") + Process.kill(new_resource.stop_signal, pid) + ::File.unlink(pid_file) + end + + def action_restart + return if options['never_restart'] + action_stop + action_start + end + + def action_reload + return if options['never_reload'] + return unless pid + Chef::Log.debug("[#{new_resource}] Reloading with #{new_resource.reload_signal}. Current PID is #{pid.inspect}.") + Process.kill(new_resource.reload_signal, pid) + end + + def pid + return nil unless ::File.exist?(pid_file) + pid = IO.read(pid_file).to_i + begin + # Check if the PID is running. + Process.kill(0, pid) + pid + rescue Errno::ESRCH + nil + end + end + + private + + def service_resource + # Intentionally not implemented. + raise NotImplementedError + end + + def enable_service + end + + # Write all major service parameters to a file so that if they change, we + # can restart the service. This also makes debuggin a bit easier so you + # can still see what it thinks it was starting without sifting through + # piles of debug output. + def create_service + service_template(run_file, 'dummy.json.erb') + end + + def disable_service + end + + # Delete the tracking file. + def destroy_service + file run_file do + action :delete + end + + file pid_file do + action :delete + end + end + + # Path to the run parameters tracking file. + def run_file + "/var/run/#{new_resource.service_name}.json" + end + + # Path to the PID file. + def pid_file + "/var/run/#{new_resource.service_name}.pid" + end + + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/inittab.rb b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/inittab.rb new file mode 100644 index 0000000..4758455 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/inittab.rb @@ -0,0 +1,150 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/util/file_edit' + +require 'poise_service/service_providers/base' + + +module PoiseService + module ServiceProviders + class Inittab < Base + provides(:inittab) + + def self.provides_auto?(node, resource) + ::File.exist?('/etc/inittab') + end + + def pid + IO.read(pid_file).to_i if ::File.exist?(pid_file) + end + + # Don't try to stop when disabling because we can't. + def action_disable + disable_service + notifying_block do + destroy_service + end + end + + def action_start + Chef::Log.debug("[#{new_resource}] Inittab services are always started.") + end + + def action_stop + raise NotImplementedError.new("[#{new_resource}] Inittab services cannot be stopped") + end + + def action_restart + return if options['never_restart'] + # Just kill it and let init restart it. + Process.kill(new_resource.stop_signal, pid) if pid + end + + def action_reload + return if options['never_reload'] + Process.kill(new_resource.reload_signal, pid) if pid + end + + private + + def service_resource + # Intentionally not implemented. + raise NotImplementedError + end + + def enable_service + end + + def disable_service + end + + def create_service + # Sigh scoping. + pid_file_ = pid_file + # Inittab only allows 127 characters for the command, so cram stuff in + # a file. Writing to a file is gross, but so is using inittab so ¯\_(ツ)_/¯. + service_template("/sbin/poise_service_#{new_resource.service_name}", 'inittab.sh.erb') do + mode '755' + variables.update( + pid_file: pid_file_, + ) + end + # Add to inittab. + edit_inittab do |content| + inittab_line = "#{service_id}:2345:respawn:/sbin/poise_service_#{new_resource.service_name}" + if content =~ /^# #{Regexp.escape(service_tag)}$/ + # Existing line, update in place. + content.gsub!(/^(# #{Regexp.escape(service_tag)}\n)(.*)$/, "\\1#{inittab_line}") + else + # Add to the end. + content << "# #{service_tag}\n#{inittab_line}\n" + end + end + end + + def destroy_service + # Remove from inittab. + edit_inittab do |content| + content.gsub!(/^# #{Regexp.escape(service_tag)}\n.*?\n$/, '') + end + + file "/sbin/poise_service_#{new_resource.service_name}" do + action :delete + end + + file pid_file do + action :delete + end + end + + # The shortened ID because sysvinit only allows 4 characters. + def service_id + # This is a terrible hash, but it should be good enough. + options['service_id'] || begin + sum = new_resource.service_name.sum(20).to_s(36) + if sum.length < 4 + 'p' + sum + else + sum + end + end + end + + # Tag to put in a comment in inittab for tracking. + def service_tag + "poise_service(#{new_resource.service_name})" + end + + def pid_file + options['pid_file'] || "/var/run/#{new_resource.service_name}.pid" + end + + def edit_inittab(&block) + inittab = IO.read('/etc/inittab') + original_inittab = inittab.dup + block.call(inittab) + if inittab != original_inittab + file '/etc/inittab' do + content inittab + end + + execute 'telinit q' + end + end + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/systemd.rb b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/systemd.rb new file mode 100644 index 0000000..1de6160 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/systemd.rb @@ -0,0 +1,85 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/shell_out' + +require 'poise_service/service_providers/base' + + +module PoiseService + module ServiceProviders + class Systemd < Base + include Chef::Mixin::ShellOut + provides(:systemd) + + # @api private + def self.provides_auto?(node, resource) + # Don't allow systemd under docker, it won't work in most cases. + return false if node['virtualization'] && %w{docker lxc}.include?(node['virtualization']['system']) + service_resource_hints.include?(:systemd) + end + + # @api private + def self.default_inversion_options(node, resource) + super.merge({ + # Automatically reload systemd on changes. + auto_reload: true, + # Service restart mode. + restart_mode: 'on-failure', + }) + end + + def pid + cmd = shell_out(%w{systemctl status} + [new_resource.service_name]) + if !cmd.error? && cmd.stdout.include?('Active: active (running)') && md = cmd.stdout.match(/Main PID: (\d+)/) + md[1].to_i + else + nil + end + end + + private + + def service_resource + super.tap do |r| + r.provider(Chef::Provider::Service::Systemd) + end + end + + def systemctl_daemon_reload + execute 'systemctl daemon-reload' do + action :nothing + user 'root' + end + end + + def create_service + reloader = systemctl_daemon_reload + service_template("/etc/systemd/system/#{new_resource.service_name}.service", 'systemd.service.erb') do + notifies :run, reloader, :immediately if options['auto_reload'] + variables.update(auto_reload: options['auto_reload'], restart_mode: options['restart_mode']) + end + end + + def destroy_service + file "/etc/systemd/system/#{new_resource.service_name}.service" do + action :delete + end + end + + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/sysvinit.rb b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/sysvinit.rb new file mode 100644 index 0000000..8438a41 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/sysvinit.rb @@ -0,0 +1,97 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise_service/service_providers/base' + + +module PoiseService + module ServiceProviders + class Sysvinit < Base + provides(:sysvinit) + + def self.provides_auto?(node, resource) + [:debian, :redhat, :invokercd].any? {|name| service_resource_hints.include?(name) } + end + + def pid + IO.read(pid_file).to_i if ::File.exist?(pid_file) + end + + private + + def service_resource + super.tap do |r| + r.provider(case node['platform_family'] + when 'debian' + Chef::Provider::Service::Debian + when 'rhel' + Chef::Provider::Service::Redhat + else + # Better than nothing I guess? Will fail on enable I think. + Chef::Provider::Service::Init + end) + r.init_command(script_path) + # Pending https://github.com/chef/chef/pull/4709. + r.start_command("#{script_path} start") + r.stop_command("#{script_path} stop") + r.status_command("#{script_path} status") + r.restart_command("#{script_path} restart") + r.reload_command("#{script_path} reload") + end + end + + def create_service + # Split the command into the binary and its arguments. This is for + # start-stop-daemon since it treats those differently. + parts = new_resource.command.split(/ /, 2) + daemon = ENV['PATH'].split(/:/) + .map {|path| ::File.absolute_path(parts[0], path) } + .find {|path| ::File.exist?(path) } || parts[0] + # Sigh scoping. + pid_file_ = pid_file + # Render the service template + service_template(script_path, 'sysvinit.sh.erb') do + mode '755' + variables.update( + daemon: daemon, + daemon_options: parts[1].to_s, + pid_file: pid_file_, + pid_file_external: options['pid_file_external'].nil? ? !!options['pid_file'] : options['pid_file_external'], + platform_family: node['platform_family'], + ) + end + end + + def destroy_service + file script_path do + action :delete + end + + file pid_file do + action :delete + end + end + + def script_path + options['script_path'] || "/etc/init.d/#{new_resource.service_name}" + end + + def pid_file + options['pid_file'] || "/var/run/#{new_resource.service_name}.pid" + end + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/upstart.rb b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/upstart.rb new file mode 100644 index 0000000..61b2318 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/service_providers/upstart.rb @@ -0,0 +1,128 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Used in the template. +require 'shellwords' + +require 'chef/mixin/shell_out' + +require 'poise_service/error' +require 'poise_service/service_providers/base' + + +module PoiseService + module ServiceProviders + class Upstart < Base + include Chef::Mixin::ShellOut + provides(:upstart) + + def self.provides_auto?(node, resource) + # Don't allow upstart under docker, it won't work. + return false if node['virtualization'] && %w{docker lxc}.include?(node['virtualization']['system']) + service_resource_hints.include?(:upstart) + end + + # True restart in Upstart preserves the original config data, we want the + # more obvious behavior like everything else in the world that restart + # would re-read the updated config file. Use stop+start to get this + # behavior. http://manpages.ubuntu.com/manpages/raring/man8/initctl.8.html + def action_restart + return if options['never_restart'] + action_stop + action_start + end + + # Shim out reload if we have a version that predates reload support. + def action_reload + return if options['never_reload'] + if !upstart_features[:reload_signal] && new_resource.reload_signal != 'HUP' + if options[:reload_shim] + Process.kill(new_resource.reload_signal, pid) + else + check_reload_signal! + end + else + super + end + end + + def pid + cmd = shell_out(%w{initctl status} + [new_resource.service_name]) + if !cmd.error? && md = cmd.stdout.match(/process (\d+)/) + md[1].to_i + else + nil + end + end + + private + + def service_resource + super.tap do |r| + r.provider(Chef::Provider::Service::Upstart) + end + end + + def create_service + check_reload_signal! + # Set features so it will be a closure below. + features = upstart_features + service_template("/etc/init/#{new_resource.service_name}.conf", 'upstart.conf.erb') do + variables.update( + upstart_features: features, + ) + end + end + + def destroy_service + file "/etc/init/#{new_resource.service_name}.conf" do + action :delete + end + end + + def upstart_version + cmd = shell_out(%w{initctl --version}) + if !cmd.error? && md = cmd.stdout.match(/upstart ([^)]+)\)/) + md[1] + else + '0' + end + end + + def upstart_features + @upstart_features ||= begin + upstart_ver = Gem::Version.new(upstart_version) + versions_added = { + kill_signal: '1.3', + reload_signal: '1.10', + setuid: '1.4', + } + versions_added.inject({}) do |memo, (feature, version)| + memo[feature] = Gem::Requirement.create(">= #{version}").satisfied_by?(upstart_ver) + memo + end + end + end + + def check_reload_signal! + if !options['reload_shim'] && !upstart_features[:reload_signal] && new_resource.reload_signal != 'HUP' + raise Error.new("Upstart #{upstart_version} only supports HUP for reload, to use the shim please set the 'reload_shim' options for #{new_resource.to_s}") + end + end + + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/utils.rb b/cookbooks/poise-service/files/halite_gem/poise_service/utils.rb new file mode 100644 index 0000000..bbf7896 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/utils.rb @@ -0,0 +1,45 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'pathname' + + +module PoiseService + # Utility methods for PoiseService. + # + # @api public + # @since 1.0.0 + module Utils + # Methods are also available as module-level methods as well as a mixin. + extend self + + # Common segments to ignore + COMMON_SEGMENTS = %w{var www current etc}.inject({}) {|memo, seg| memo[seg] = true; memo } + + # Parse the service name from a path. Look at the last component of the + # path, ignoring some common names. + # + # @param path [String] Path to parse. + # @return [String] + # @example + # attribute(:service_name, kind_of: String, default: lazy { PoiseService::Utils.parse_service_name(path) }) + def parse_service_name(path) + parts = Pathname.new(path).each_filename.to_a.reverse! + # Find the last segment not in common segments, fall back to the last segment. + parts.find {|seg| !COMMON_SEGMENTS[seg] } || parts.first + end + end +end diff --git a/cookbooks/poise-service/files/halite_gem/poise_service/version.rb b/cookbooks/poise-service/files/halite_gem/poise_service/version.rb new file mode 100644 index 0000000..6859904 --- /dev/null +++ b/cookbooks/poise-service/files/halite_gem/poise_service/version.rb @@ -0,0 +1,20 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module PoiseService + VERSION = '1.4.2' +end diff --git a/cookbooks/poise-service/libraries/default.rb b/cookbooks/poise-service/libraries/default.rb new file mode 100644 index 0000000..dd4c6ca --- /dev/null +++ b/cookbooks/poise-service/libraries/default.rb @@ -0,0 +1,19 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) +require "poise_service/cheftie" diff --git a/cookbooks/poise-service/metadata.json b/cookbooks/poise-service/metadata.json new file mode 100644 index 0000000..8a3dc4c --- /dev/null +++ b/cookbooks/poise-service/metadata.json @@ -0,0 +1 @@ +{"name":"poise-service","version":"1.4.2","description":"A Chef cookbook for managing system services.","long_description":"# Poise-Service Cookbook\n\n[![Build Status](https://img.shields.io/travis/poise/poise-service.svg)](https://travis-ci.org/poise/poise-service)\n[![Gem Version](https://img.shields.io/gem/v/poise-service.svg)](https://rubygems.org/gems/poise-service)\n[![Cookbook Version](https://img.shields.io/cookbook/v/poise-service.svg)](https://supermarket.chef.io/cookbooks/poise-service)\n[![Coverage](https://img.shields.io/codecov/c/github/poise/poise-service.svg)](https://codecov.io/github/poise/poise-service)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/poise-service.svg)](https://gemnasium.com/poise/poise-service)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\nA [Chef](https://www.chef.io/) cookbook to provide a unified interface for\nservices.\n\n### What is poise-service?\n\nPoise-service is a tool for developers of \"library cookbooks\" to define a\nservice without forcing the end-user of the library to adhere to their choice of\nservice management framework. The `poise_service` resource represents an\nabstract service to be run, which can then be customized by node attributes and\nthe `poise_service_options` resource. This is a technique called [dependency\ninjection](https://en.wikipedia.org/wiki/Dependency_injection), and allows a\nmeasure of decoupling between the library and application cookbooks.\n\n### Why would I use poise-service?\n\nPoise-service is most useful for authors of library-style cookbooks, for example\nthe `apache2`, `mysql`, or `application` cookbooks. When using other service\nmanagement options with Chef, the author of the library cookbook has to add\nspecific code for each service management framework they want to support, often\nresulting in a cookbook only supporting the favorite framework of the author or\ndepending on distribution packages for their init scripts. The `poise_service`\nresource allows library cookbook authors a way to write generic code for all\nservice management frameworks while still allowing users of that cookbook to\nchoose which service management framework best fits their needs.\n\n### How is this different from the built-in service resource?\n\nChef includes a `service` resource which allows interacting with certain\nservice management frameworks such as SysV, Upstart, and systemd.\n`poise-service` goes further in that it actually generates the configuration\nfiles needed for the requested service management framework, as well as offering\na dependency injection system for application cookbooks to customize which\nframework is used.\n\n### What service management frameworks are supported?\n\n* [SysV (aka /etc/init.d)](#sysvinit)\n* [Upstart](#upstart)\n* [systemd](#systemd)\n* [Inittab](#inittab)\n* [Runit](https://github.com/poise/poise-service-runit)\n* [Monit](https://github.com/poise/poise-monit#service-provider)\n* [Solaris](https://github.com/sh9189/poise-service-solaris)\n* [AIX](https://github.com/johnbellone/poise-service-aix)\n* *Supervisor (coming soon!)*\n\n\n## Quick Start\n\nTo create a service user and a service to run Apache2:\n\n```ruby\npoise_service_user 'www-data'\n\npoise_service 'apache2' do\n command '/usr/sbin/apache2 -f /etc/apache2/apache2.conf -DFOREGROUND'\n stop_signal 'WINCH'\n reload_signal 'USR1'\nend\n```\n\nor for a hypothetical Rails web application:\n\n```ruby\npoise_service_user 'myapp'\n\npoise_service 'myapp-web' do\n command 'bundle exec unicorn -p 8080'\n user 'myapp'\n directory '/srv/myapp'\n environment RAILS_ENV: 'production'\nend\n```\n\n## Resources\n\n### `poise_service`\n\nThe `poise_service` resource is the abstract definition of a service.\n\n```ruby\npoise_service 'myapp' do\n command 'myapp --serve'\n environment RAILS_ENV: 'production'\nend\n```\n\n#### Actions\n\n* `:enable` – Create, enable and start the service. *(default)*\n* `:disable` – Stop, disable, and destroy the service.\n* `:start` – Start the service.\n* `:stop` – Stop the service.\n* `:restart` – Stop and then start the service.\n* `:reload` – Send the configured reload signal to the service.\n\n#### Attributes\n\n* `service_name` – Name of the service. *(name attribute)*\n* `command` – Command to run for the service. This command must stay in the\n foreground and not daemonize itself. *(required)*\n* `user` – User to run the service as. See\n [`poise_service_user`](#poise_service_user) for any easy way to create service\n users. *(default: root)*\n* `directory` – Working directory for the service. *(default: home directory for\n user, or / if not found)*\n* `environment` – Environment variables for the service.\n* `stop_signal` – Signal to use to stop the service. Some systems will fall back\n to SIGKILL if this signal fails to stop the process. *(default: TERM)*\n* `reload_signal` – Signal to use to reload the service. *(default: HUP)*\n* `restart_on_update` – If true, the service will be restarted if the service\n definition or configuration changes. If `'immediately'`, the notification will\n happen in immediate mode. *(default: true)*\n\n#### Service Options\n\nThe `poise-service` library offers an additional way to pass configuration\ninformation to the final service called \"options\". Options are key/value pairs\nthat are passed down to the service provider and can be used to control how it\ncreates and manages the service. These can be set in the `poise_service`\nresource using the `options` method, in node attributes or via the\n`poise_service_options` resource. The options from all sources are merged\ntogether in to a single hash.\n\nWhen setting options in the resource you can either set them for all providers:\n\n```ruby\npoise_service 'myapp' do\n command 'myapp --serve'\n options status_port: 8000\nend\n```\n\nor for a single provider:\n\n```ruby\npoise_service 'myapp' do\n command 'myapp --serve'\n options :systemd, after_target: 'network'\nend\n```\n\nSetting via node attributes is generally how an end-user or application cookbook\nwill set options to customize services in the library cookbooks they are using.\nYou can set options for all services or for a single service, by service name\nor by resource name:\n\n```ruby\n# Global, for all services.\noverride['poise-service']['options']['after_target'] = 'network'\n# Single service.\noverride['poise-service']['myapp']['template'] = 'myapp.erb'\n```\n\nThe `poise_service_options` resource is also available to set node attributes\nfor a specific service in a DSL-friendly way:\n\n```ruby\npoise_service_options 'myapp' do\n template 'myapp.erb'\n restart_on_update false\nend\n```\n\nUnlike resource attributes, service options can be different for each provider.\nNot all providers support the same options so make sure to check the\ndocumentation for each provider to see what options are available.\n\n### `poise_service_options`\n\nThe `poise_service_options` resource allows setting per-service options in a\nDSL-friendly way. See [the Service Options](#service-options) section for more\ninformation about service options overall.\n\n```ruby\npoise_service_options 'myapp' do\n template 'myapp.erb'\n restart_on_update false\nend\n```\n\n#### Actions\n\n* `:run` – Apply the service options. *(default)*\n\n#### Attributes\n\n* `resource` – Name of the service. *(name attribute)*\n* `for_provider` – Provider to set options for.\n\nAll other attribute keys will be used as options data.\n\n### `poise_service_user`\n\nThe `poise_service_user` resource is an easy way to create service users. It is\nnot required to use `poise_service`, it is only a helper.\n\n```ruby\npoise_service_user 'myapp' do\n home '/srv/myapp'\nend\n```\n\n#### Actions\n\n* `:create` – Create the user and group. *(default)*\n* `:remove` – Remove the user and group.\n\n#### Attributes\n\n* `user` – Name of the user. *(name attribute)*\n* `group` – Name of the group. Set to `false` to disable group creation. *(name attribute)*\n* `uid` – UID of the user. *(default: automatic)*\n* `gid` – GID of the group. *(default: automatic)*\n* `home` – Home directory of the user.\n* `shell` – Shell of the user. *(default: /bin/nologin if present or /bin/false)*\n\n## Providers\n\n### `sysvinit`\n\nThe `sysvinit` provider supports SystemV-style init systems on Debian-family and\nRHEL-family platforms. It will create the `/etc/init.d/` script\nand enable/disable the service using the platform-specific service resource.\n\n```ruby\npoise_service 'myapp' do\n provider :sysvinit\n command 'myapp --serve'\nend\n```\n\nBy default a PID file will be created in `/var/run/service_name.pid`. You can\nuse the `pid_file` option detailed below to override this and rely on your\nprocess creating a PID file in the given path.\n\n#### Options\n\n* `pid_file` – Path to PID file that the service command will create.\n* `pid_file_external` – If true, assume the service will create the PID file\n itself. *(default: true if `pid_file` option is set)*\n* `template` – Override the default script template. If you want to use a\n template in a different cookbook use `'cookbook:template'`.\n* `command` – Override the service command.\n* `directory` – Override the service directory.\n* `environment` – Override the service environment variables.\n* `reload_signal` – Override the service reload signal.\n* `stop_signal` – Override the service stop signal.\n* `user` – Override the service user.\n* `never_restart` – Never try to restart the service.\n* `never_reload` – Never try to reload the service.\n* `script_path` – Override the path to the generated service script.\n\n### `upstart`\n\nThe `upstart` provider supports [Upstart](http://upstart.ubuntu.com/). It will\ncreate the `/etc/init/service_name.conf` configuration.\n\n```ruby\npoise_service 'myapp' do\n provider :upstart\n command 'myapp --serve'\nend\n```\n\nAs a wide variety of versions of Upstart are in use in various Linux\ndistributions, the provider does its best to identify which features are\navailable and provide shims as appropriate. Most of these should be invisible\nhowever Upstart older than 1.10 does not support setting a `reload signal` so\nonly SIGHUP can be used. You can set a `reload_shim` option to enable an\ninternal implementaion of reloading to be used for signals other than SIGHUP,\nhowever as this is implemented inside Chef code, running `initctl reload` would\nstill result in SIGHUP being sent. For this reason, the feature is disabled by\ndefault and will throw an error if a reload signal other than SIGHUP is used.\n\n#### Options\n\n* `reload_shim` – Enable the reload signal shim. See above for a warning about\n this feature.\n* `template` – Override the default configuration template. If you want to use a\n template in a different cookbook use `'cookbook:template'`.\n* `command` – Override the service command.\n* `directory` – Override the service directory.\n* `environment` – Override the service environment variables.\n* `reload_signal` – Override the service reload signal.\n* `stop_signal` – Override the service stop signal.\n* `user` – Override the service user.\n* `never_restart` – Never try to restart the service.\n* `never_reload` – Never try to reload the service.\n\n### `systemd`\n\nThe `systemd` provider supports [systemd](http://www.freedesktop.org/wiki/Software/systemd/).\nIt will create the `/etc/systemd/system/service_name.service` configuration.\n\n\n```ruby\npoise_service 'myapp' do\n provider :systemd\n command 'myapp --serve'\nend\n```\n\n#### Options\n\n* `template` – Override the default configuration template. If you want to use a\n template in a different cookbook use `'cookbook:template'`.\n* `command` – Override the service command.\n* `directory` – Override the service directory.\n* `environment` – Override the service environment variables.\n* `reload_signal` – Override the service reload signal.\n* `stop_signal` – Override the service stop signal.\n* `user` – Override the service user.\n* `never_restart` – Never try to restart the service.\n* `never_reload` – Never try to reload the service.\n* `auto_reload` – Run `systemctl daemon-reload` after changes to the unit file. *(default: true)*\n* `restart_mode` – Restart mode for the generated service unit. *(default: on-failure)*\n\n### `inittab`\n\nThe `inittab` provider supports managing services via `/etc/inittab` using\n[SystemV Init](http://www.nongnu.org/sysvinit/). This can provide basic\nprocess supervision even on very old *nix machines.\n\n```ruby\npoise_service 'myapp' do\n provider :inittab\n command 'myapp --serve'\nend\n```\n\n**NOTE:** Inittab does not allow stopping services, and they are started as soon\nas they are enabled.\n\n#### Options\n\n* `never_restart` – Never try to restart the service.\n* `never_reload` – Never try to reload the service.\n* `pid_file` – Path to PID file that the service command will create.\n* `service_id` – Unique 1-4 character tag for the service. Defaults to an\n auto-generated hash based on the service name. If these collide, bad things\n happen. Don't do that.\n\n## ServiceMixin\n\nFor the common case of a resource (LWRP or plain Ruby) that roughly maps to\n\"some config files and a service\" poise-service provides a mixin module,\n`PoiseService::ServiceMixin`. This mixin adds the standard service actions\n(`enable`, `disable`, `start`, `stop`, `restart`, and `reload`) with basic\nimplementations that call those actions on a `poise_service` resource for you.\nYou customize the service by defining a `service_options` method on your\nprovider class:\n\n```ruby\ndef service_options(service)\n # service is the PoiseService::Resource object instance.\n service.command \"/usr/sbin/#{new_resource.name} -f /etc/#{new_resource.name}/conf/httpd.conf -DFOREGROUND\"\n service.stop_signal 'WINCH'\n service.reload_signal 'USR1'\nend\n```\n\nYou will generally want to override the `enable` action to install things\nrelated to the service like packages, users and configuration files:\n\n```ruby\ndef action_enable\n notifying_block do\n package 'apache2'\n poise_service_user 'www-data'\n template \"/etc/#{new_resource.name}/conf/httpd.conf\" do\n # ...\n end\n end\n # This super call will run the normal service enable,\n # creating the service and starting it.\n super\nend\n```\n\nSee [the poise_service_test_mixin resource](test/cookbooks/poise-service_test/resources/mixin.rb)\nand [provider](test/cookbooks/poise-service_test/providers/mixin.rb) for\nexamples of using `ServiceMixin` in an LWRP.\n\n## Sponsors\n\nDevelopment sponsored by [Bloomberg](http://www.bloomberg.com/company/technology/).\n\nThe Poise test server infrastructure is sponsored by [Rackspace](https://rackspace.com/).\n\n## License\n\nCopyright 2015-2016, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainer":"Noah Kantrowitz","maintainer_email":"noah@coderanger.net","license":"Apache 2.0","platforms":{},"dependencies":{"poise":"~> 2.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{},"source_url":"https://github.com/poise/poise-service","issues_url":"https://github.com/poise/poise-service/issues","chef_version":"~> 12","ohai_version":{}} \ No newline at end of file diff --git a/cookbooks/poise-service/templates/default/dummy.json.erb b/cookbooks/poise-service/templates/default/dummy.json.erb new file mode 100644 index 0000000..ad62f40 --- /dev/null +++ b/cookbooks/poise-service/templates/default/dummy.json.erb @@ -0,0 +1,7 @@ +<%= {command: @command, + directory: @directory, + environment: @environment, + name: @name, + reload_signal: @reload_signal, + stop_signal: @stop_signal, + user: @user}.to_json %> diff --git a/cookbooks/poise-service/templates/default/inittab.sh.erb b/cookbooks/poise-service/templates/default/inittab.sh.erb new file mode 100644 index 0000000..7e19bf7 --- /dev/null +++ b/cookbooks/poise-service/templates/default/inittab.sh.erb @@ -0,0 +1,15 @@ +#!/bin/sh +exec /opt/chef/embedded/bin/ruby <", Process.pid) +Dir.chdir("<%= @directory %>") +ent = Etc.getpwnam("<%= @user %>") +if Process.euid != ent.uid || Process.egid != ent.gid + Process.initgroups(ent.name, ent.gid) + Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid + Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid +end +(ENV["HOME"] = Dir.home("<%= @user %>")) rescue nil +<%= @environment.map {|key, value| "ENV[#{key.to_s.inspect}] = #{value.to_s.inspect}" }.join("; ") %> +exec(*<%= Shellwords.split(@command).inspect %>) +EOH diff --git a/cookbooks/poise-service/templates/default/systemd.service.erb b/cookbooks/poise-service/templates/default/systemd.service.erb new file mode 100644 index 0000000..ebdf871 --- /dev/null +++ b/cookbooks/poise-service/templates/default/systemd.service.erb @@ -0,0 +1,14 @@ +[Unit] +Description=<%= @name %> + +[Service] +Environment=<%= @environment.map {|key, val| %Q{"#{key}=#{val}"} }.join(' ') %> +ExecStart=<%= @command %> +ExecReload=/bin/kill -<%= @reload_signal %> $MAINPID +KillSignal=<%= @stop_signal %> +User=<%= @user %> +WorkingDirectory=<%= @directory %> +Restart=<%= @restart_mode %> + +[Install] +WantedBy=multi-user.target diff --git a/cookbooks/poise-service/templates/default/sysvinit.sh.erb b/cookbooks/poise-service/templates/default/sysvinit.sh.erb new file mode 100644 index 0000000..f13d811 --- /dev/null +++ b/cookbooks/poise-service/templates/default/sysvinit.sh.erb @@ -0,0 +1,190 @@ +#!/bin/sh +# Init script for <%= @name %> generated by poise-service +# +### BEGIN INIT INFO +# Provides: <%= @name %> +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Init script for <%= @name %> +# Description: Init script for <%= @name %> +### END INIT INFO + +<%- if @platform_family == 'debian' -%> +. /lib/lsb/init-functions + +_start() { + start-stop-daemon --start --quiet --background \ + --pidfile "<%= @pid_file %>"<% unless @pid_file_external %> --make-pidfile<% end %> \ + --chuid "<%= @user %>" --chdir "<%= @directory %>" \ + --exec "<%= @daemon %>" -- <%= @daemon_options %> +} + +_stop() { + start-stop-daemon --stop --quiet --pidfile "<%= @pid_file %>" --user "<%= @user %>" --signal "<%= @stop_signal %>" +} + +_status() { + status_of_proc -p "<%= @pid_file %>" "<%= @daemon %>" "<%= @name %>" +} + +_reload() { + start-stop-daemon --stop --quiet --pidfile "<%= @pid_file %>" --user "<%= @user %>" --signal "<%= @reload_signal %>" +} + +<%- else -%> +_start() { + <%# Implementing this using RedHat's bash helpers is too painful. Sorry. %> + <%# See dummy.rb for a more commented version of this code. %> + /opt/chef/embedded/bin/ruby < +File.unlink(pid_file) if File.exist?(pid_file) +if Process.fork + sleep(1) until File.exist?(pid_file) +else + Process.daemon(true) + Dir.chdir(<%= @directory.inspect %>) + <%- unless @pid_file_external -%> + IO.write(pid_file, Process.pid) + <%- end -%> + ent = Etc.getpwnam(<%= @user.inspect %>) + if Process.euid != ent.uid || Process.egid != ent.gid + Process.initgroups(ent.name, ent.gid) + Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid + Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid + end + Kernel.exec(*<%= Shellwords.split(@command).inspect %>) + exit! +end +EOH +} + +_stop() { + if [ -r "<%= @pid_file %>" ]; then + kill -<%= @stop_signal%> "$(cat "<%= @pid_file %>")" + else + return 0 + fi +} + +_status() { + if [ -r "<%= @pid_file %>" ]; then + kill -0 "$(cat "<%= @pid_file %>")" + else + return 1 + fi +} + +_reload() { + if [ -r "<%= @pid_file %>" ]; then + kill -<%= @reload_signal%> "$(cat "<%= @pid_file %>")" + else + return 1 + fi +} + +<%# Some functions to match LSB %> + +log_daemon_msg() { + echo -n "$1" +} + +log_progress_msg() { + echo -n "$1" +} + +log_warning_msg() { + echo -n "$1" +} + +log_failure_msg() { + echo -n "$1" +} + +log_end_msg() { + if [ "$1" = 0 ]; then + echo " [ OK ]" + else + echo " [FAILED]" + fi +} +<%- end -%> + +set -e + +start() { + if _start + then + rc=0 + sleep 1 + if ! kill -0 "$(cat "<%= @pid_file %>")" >/dev/null 2>&1; then + log_failure_msg "<%= @name %> failed to start" + rc=1 + fi + else + rc=1 + fi + if [ "$rc" -eq 0 ]; then + log_end_msg 0 + else + log_end_msg 1 + rm -f "<%= @pid_file %>" + fi +} + +<%- @environment.each do |key, val| -%> +export <%= key %>="<%= val %>" +<%- end -%> +export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" + +case "$1" in + start) + log_daemon_msg "Starting <%= @name %>" + if [ -s "<%= @pid_file %>" ] && kill -0 "$(cat "<%= @pid_file %>")" >/dev/null 2>&1; then + log_progress_msg "apparently already running" + log_end_msg 0 + exit 0 + fi + start + ;; + + stop) + log_daemon_msg "Stopping <%= @name %>" + _stop + log_end_msg "$?" + rm -f "<%= @pid_file %>" + ;; + + reload|force-reload) + log_daemon_msg "Reloading <%= @name %>" + _reload + log_end_msg "$?" + ;; + + restart) + set +e + log_daemon_msg "Restarting <%= @name %>" + if [ -s "<%= @pid_file %>" ] && kill -0 "$(cat "<%= @pid_file %>")" >/dev/null 2>&1; then + _stop || true + sleep 1 + else + log_warning_msg "<%= @name %> not running, attempting to start." + rm -f "<%= @pid_file %>" + fi + start + ;; + + status) + set +e + _status + exit $? + ;; + + *) + echo "Usage: /etc/init.d/<%= @name %> {start|stop|reload|force-reload|restart|status}" + exit 1 +esac + +exit 0 diff --git a/cookbooks/poise-service/templates/default/upstart.conf.erb b/cookbooks/poise-service/templates/default/upstart.conf.erb new file mode 100644 index 0000000..cd60ea1 --- /dev/null +++ b/cookbooks/poise-service/templates/default/upstart.conf.erb @@ -0,0 +1,49 @@ +# <%= @name %> generated by poise-service for <%= @new_resource.to_s %> + +description "<%= @name %>" + +start on runlevel [2345] +stop on runlevel [!2345] + +respawn +respawn limit 10 5 +umask 022 +chdir <%= @directory %> +<%- @environment.each do |key, val| -%> +env <%= key %>="<%= val %>" +<%- end -%> +<%- if @upstart_features[:setuid] -%> +setuid <%= @user %> +<%- end -%> +<%- if @upstart_features[:kill_signal] -%> +kill signal <%= @stop_signal %> +<%- end -%> +<%- if @upstart_features[:reload_signal] -%> +reload signal <%= @reload_signal %> +<%- end -%> + +<%- if @upstart_features[:setuid] -%> +exec <%= @command %> +<%- else -%> +script +exec /opt/chef/embedded/bin/ruby <) +if Process.euid != ent.uid || Process.egid != ent.gid + Process.initgroups(ent.name, ent.gid) + Process::GID.change_privilege(ent.gid) if Process.egid != ent.gid + Process::UID.change_privilege(ent.uid) if Process.euid != ent.uid +end +ENV["HOME"] = Dir.home(<%= @user.inspect %>) rescue nil +exec(*<%= Shellwords.split(@command).inspect %>) +EOH +end script +<%- end -%> +<%- if !@upstart_features[:kill_signal] && @stop_signal != 'TERM' -%> +pre-stop script + PID=`initctl status <%= @name %> | sed 's/^.*process \([0-9]*\)$/\1/'` + if [ -n "$PID" ]; then + kill -<%= @stop_signal %> "$PID" + fi +end script +<%- end -%> diff --git a/cookbooks/poise/CHANGELOG.md b/cookbooks/poise/CHANGELOG.md new file mode 100644 index 0000000..a856e2c --- /dev/null +++ b/cookbooks/poise/CHANGELOG.md @@ -0,0 +1,187 @@ +# Changelog + +## v2.7.2 + +* Test harness fixes for Chef. + +## v2.7.1 + +* Minor tweak for compatability with Chef master. + +## v2.7.0 + +* More compatibility improvements for Chef 12.9. +* New helper: `Poise::Helpers::Win32User` to automatically convert `'root'` + defaults for user and group properties to more platform-appropriate values. +* Enhanced `poise_shell_out` to better cope with Windows command parsing. Use + Bash-style commands and it will automatically convert. +* Overall compatibility fixes for Windows. + +## v2.6.1 + +* Compatibility with Chef master to fix issues with `defined_in!` not ignoring + stack frames from Chef code. +* Setting a provider in a inversion options resource now works as (probably) + expected. + +## v2.6.0 + +* New backwards-compatibility helper: `Poise::Backports::VERIFY_PATH`. Use it + like `verify "myapp -t #{Poise::Backports::VERIFY_PATH}" if defined?(verify)` + for backwards-compatible usage of file verifications. +* Fixed Poise's implementation of lazy defaults to more closely match Chef's + even when both are used in conjunction. Lazy defaults will no longer be + evaluated when setting a value or getting an existing non-default value. + +## v2.5.0 + +* New property for inversion resources: `provider_no_auto`. Set one or more + provider names that will be ignored for automatic resolution for that instance. +* Support `variables` as an alias for `options` in template content properties + to match the `template` resource. +* Template content properties are no longer validated after creation for + non-default actions. +* Formalize the extra-verbose logging mode for Poise and expose it via helpers. +* Extra-verbose logging mode can now be enabled by creating a `/poise_debug` file. +* New helper: `poise_shell_out`. Like normal `shell_out` but sets group and + environment variables automatically to better defaults. + +## v2.4.0 + +* Added return value to `Container#register_subresource` to track if the resource + was already added. +* Improve inspect output for subresources and containers. +* Ensure notifications work with subresources. +* Inversion providers process name equivalences. + +## v2.3.2 + +* Improve handling of deeply nested subresources. + +## v2.3.1 + +* Ensure a container with a parent link to its own type doesn't use self as the + default parent. +* Improve handling of `load_current_resource` in providers that call it via + `super`. + +## v2.3.0 + +* New helper: `ResourceSubclass`, a helper for subclassing a resource while + still using the providers as the base class. +* New feature: Non-default containers. Use `container_default: false` to mark + a container class as ineligible for default lookup. +* New feature: parent attribute defaults. You can set a `parent_default` to + provide a default value for the parent of a resource. This supports the + `lazy { }` helper as with normal default values. +* New feature: use `forced_keys: [:name]` on an option collector property to + force keys that would otherwise be clobbered by resource methods. +* Can enable verbose logging mode via a node attribute in addition to an + environment variable. + +## v2.2.3 + +* Add `ancestor_send` utility method for use in other helpers. +* Improve subresource support for use in mixins. + +## v2.2.2 + +* Fix 2.2.1 for older versions of Chef. + +## v2.2.1 + +* Fixed delayed notifications inside `notifying_block`. +* Default actions as expected within LWRPs. + +## v2.2.0 + +* Compatibility with Chef 12.4.1 and Chefspec 4.3.0. +* New helper `ResourceCloning`: Disables resource cloning between Poise-based + resources. This is enabled by default. +* Subresource parent references can be set to nil. + +## v2.1.0 + +* Compatibility with Chef 12.4. +* Add `#property` as an alias for `#attribute` in resources. This provides + forward compatibility with future versions of Chef. +* Freeze default resource attribute values. **This may break your code**, + however this is not a major release because any code broken by this change + was itself already a bug. + +## v2.0.1 + +* Make the ChefspecHelpers helper a no-op if chefspec is not already loaded. +* Fix for finding the correct cookbook for a file when using vendored gems. +* New flag for the OptionCollector helper, `parser`: + +```ruby +class Resource < Chef::Resource + include Poise + attribute(:options, option_collector: true, parser: proc {|val| parse(val) }) + + def parse(val) + {name: val} + end +end +``` + +* Fix for a possible infinite loop when using `ResourceProviderMixin` in a nested + module structure. + +## v2.0.0 + +Major overhaul! Poise is now a Halite gem/cookbook. New helpers: + +* ChefspecMatchers – Automatically create Chefspec matchers for Poise resources. +* DefinedIn – Track which file (and cookbook) a resource or provider is defined in. +* Fused – Experimental support for defining provider actions in the resource class. +* Inversion – Support for end-user dependency inversion with providers. + +All helpers are compatible with Chef >= 12.0. Chef 11 is now deprecated, if you +need to support Chef 11 please continue to use Poise 1. + +## v1.0.12 + +* Correctly propagate errors from inside notifying_block. + +## v1.0.10 + +* Fixes an issue with the LWRPPolyfill helper and false values. + + +## v1.0.8 + +* Delayed notifications from nested converges will still only run at the end of + the main converge. + +## v1.0.6 + +* The include_recipe helper now works correctly when used at compile time. + +## v1.0.4 + +* Redeclaring a template attribute with the same name as a parent class will + inherit its options. + +## v1.0.2 + +* New template attribute pattern. + +```ruby +attribute(:config, template: true) + +... + +resource 'name' do + config_source 'template.erb' +end + +... + +new_resource.config_content +``` + +## v1.0.0 + +* Initial release! diff --git a/cookbooks/poise/README.md b/cookbooks/poise/README.md new file mode 100644 index 0000000..881b5cf --- /dev/null +++ b/cookbooks/poise/README.md @@ -0,0 +1,233 @@ +# Poise + +[![Build Status](https://img.shields.io/travis/poise/poise.svg)](https://travis-ci.org/poise/poise) +[![Gem Version](https://img.shields.io/gem/v/poise.svg)](https://rubygems.org/gems/poise) +[![Cookbook Version](https://img.shields.io/cookbook/v/poise.svg)](https://supermarket.chef.io/cookbooks/poise) +[![Coverage](https://img.shields.io/codecov/c/github/poise/poise.svg)](https://codecov.io/github/poise/poise) +[![Gemnasium](https://img.shields.io/gemnasium/poise/poise.svg)](https://gemnasium.com/poise/poise) +[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +## What is Poise? + +The poise cookbook is a set of libraries for writing reusable cookbooks. It +provides helpers for common patterns and a standard structure to make it easier to create flexible cookbooks. + +## Writing your first resource + +Rather than LWRPs, Poise promotes the idea of using normal, or "heavy weight" +resources, while including helpers to reduce much of boilerplate needed for this. Each resource goes in its own file under `libraries/` named to match +the resource, which is in turn based on the class name. This means that the file `libraries/my_app.rb` would contain `Chef::Resource::MyApp` which maps to the resource `my_app`. + +An example of a simple shell to start from: + +```ruby +require 'poise' +require 'chef/resource' +require 'chef/provider' + +module MyApp + class Resource < Chef::Resource + include Poise + provides(:my_app) + actions(:enable) + + attribute(:path, kind_of: String) + # Other attribute definitions. + end + + class Provider < Chef::Provider + include Poise + provides(:my_app) + + def action_enable + notifying_block do + ... # Normal Chef recipe code goes here + end + end + end +end +``` + +Starting from the top, first we require the libraries we will be using. Then we +create a module to hold our resource and provider. If your cookbook declares +multiple resources and/or providers, you might want additional nesting here. +Then we declare the resource class, which inherits from `Chef::Resource`. This +is similar to the `resources/` file in an LWRP, and a similar DSL can be used. +We then include the `Poise` mixin to load our helpers, and then call +`provides(:my_app)` to tell Chef this class will implement the `my_app` +resource. Then we use the familiar DSL, though with a few additions we'll cover +later. + +Then we declare the provider class, again similar to the `providers/` file in an +LWRP. We include the `Poise` mixin again to get access to all the helpers and +call `provides()` to tell Chef what provider this is. Rather than use the +`action :enable do ... end` DSL from LWRPs, we just define the action method +directly. The implementation of action comes from a block of recipe code +wrapped with `notifying_block` to capture changes in much the same way as +`use_inline_resources`, see below for more information about all the features of +`notifying_block`. + +We can then use this resource like any other Chef resource: + +```ruby +my_app 'one' do + path '/tmp' +end +``` + +## Helpers + +While not exposed as a specific method, Poise will automatically set the +`resource_name` based on the class name. + +### Notifying Block + +As mentioned above, `notifying_block` is similar to `use_inline_resources` in LWRPs. Any Chef resource created inside the block will be converged in a sub-context and if any have updated it will trigger notifications on the current resource. Unlike `use_inline_resources`, resources inside the sub-context can still see resources outside of it, with lookups propagating up sub-contexts until a match is found. Also any delayed notifications are scheduled to run at the end of the main converge cycle, instead of the end of this inner converge. + +This can be used to write action methods using the normal Chef recipe DSL, while still offering more flexibility through subclassing and other forms of code reuse. + +### Include Recipe + +In keeping with `notifying_block` to implement action methods using the Chef DSL, Poise adds an `include_recipe` helper to match the method of the same name in recipes. This will load and converge the requested recipe. + +### Resource DSL + +To make writing resource classes easier, Poise exposes a DSL similar to LWRPs for defining actions and attributes. Both `actions` and +`default_action` are just like in LWRPs, though `default_action` is rarely needed as the first action becomes the default. `attribute` is also available just like in LWRPs, but with some enhancements noted below. + +One notable difference over the standard DSL method is that Poise attributes +can take a block argument. + +#### Template Content + +A common pattern with resources is to allow passing either a template filename or raw file content to be used in a configuration file. Poise exposes a new attribute flag to help with this behavior: + +```ruby +attribute(:name, template: true) +``` + +This creates four methods on the class, `name_source`, `name_cookbook`, +`name_content`, and `name_options`. If the name is set to `''`, no prefix is applied to the function names. The content method can be set directly, but if not set and source is set, then it will render the template and return it as a string. Default values can also be set for any of these: + +```ruby +attribute(:name, template: true, default_source: 'app.cfg.erb', + default_options: {host: 'localhost'}) +``` + +As an example, you can replace this: + +```ruby +if new_resource.source + template new_resource.path do + source new_resource.source + owner 'app' + group 'app' + variables new_resource.options + end +else + file new_resource.path do + content new_resource.content + owner 'app' + group 'app' + end +end +``` + +with simply: + +```ruby +file new_resource.path do + content new_resource.content + owner 'app' + group 'app' +end +``` + +As the content method returns the rendered template as a string, this can also +be useful within other templates to build from partials. + +#### Lazy Initializers + +One issue with Poise-style resources is that when the class definition is executed, Chef hasn't loaded very far so things like the node object are not +yet available. This means setting defaults based on node attributes does not work directly: + +```ruby +attribute(:path, default: node['myapp']['path']) +... +NameError: undefined local variable or method 'node' +``` + +To work around this, Poise extends the idea of lazy initializers from Chef recipes to work with resource definitions as well: + +```ruby +attribute(:path, default: lazy { node['myapp']['path'] }) +``` + +These initializers are run in the context of the resource object, allowing +complex default logic to be moved to a method if desired: + +```ruby +attribute(:path, default: lazy { my_default_path }) + +def my_default_path + ... +end +``` + +#### Option Collector + +Another common pattern with resources is to need a set of key/value pairs for +configuration data or options. This can done with a simple Hash, but an option collector attribute can offer a nicer syntax: + +```ruby +attribute(:mydata, option_collector: true) +... + +my_app 'name' do + mydata do + key1 'value1' + key2 'value2' + end +end +``` + +This will be converted to `{key1: 'value1', key2: 'value2'}`. You can also pass a Hash to an option collector attribute just as you would with a normal attribute. + +## Debugging Poise + +Poise has its own extra-verbose level of debug logging that can be enabled in +three different ways. You can either set the environment variable `$POISE_DEBUG`, +set a node attribute `node['POISE_DEBUG']`, or touch the file `/POISE_DEBUG`. +You will see a log message `Extra verbose logging enabled` at the start of the +run to confirm Poise debugging has been enabled. Make sure you also set Chef's +log level to `debug`, usually via `-l debug` on the command line. + +## Upgrading from Poise 1.x + +The biggest change when upgrading from Poise 1.0 is that the mixin is no longer +loaded automatically. You must add `require 'poise'` to your code is you want to +load it, as you would with normal Ruby code outside of Chef. It is also highly +recommended to add `provides(:name)` calls to your resources and providers, this +will be required in Chef 13 and will display a deprecation warning if you do +not. This also means you can move your code out of the `Chef` module namespace +and instead declare it in your own namespace. An example of this is shown above. + +## Sponsors + +The Poise test server infrastructure is generously sponsored by [Rackspace](https://rackspace.com/). Thanks Rackspace! + +## License + +Copyright 2013-2016, Noah Kantrowitz + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cookbooks/poise/files/halite_gem/poise.rb b/cookbooks/poise/files/halite_gem/poise.rb new file mode 100644 index 0000000..0c70c74 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise.rb @@ -0,0 +1,108 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' +require 'chef/resource' +require 'chef/run_context' + +require 'poise/utils/resource_provider_mixin' + + +module Poise + include Poise::Utils::ResourceProviderMixin + autoload :Backports, 'poise/backports' + autoload :Helpers, 'poise/helpers' + autoload :NOT_PASSED, 'poise/backports/not_passed' + autoload :Provider, 'poise/provider' + autoload :Resource, 'poise/resource' + autoload :Subcontext, 'poise/subcontext' + autoload :Utils, 'poise/utils' + autoload :VERSION, 'poise/version' + + # Check if Poise's extra debugging output is enabled. This produces a *lot* + # of logging. + # + # @param node [Chef::Node, Chef::RunContext] Optional node to check for + # attributes. If not given, Chef.node is used instead. + # @return [Boolean] + def self.debug?(node=nil) + node = node.node if node.is_a?(Chef::RunContext) + node ||= Chef.node if defined?(Chef.node) + @debug_file_upper = ::File.exist?('/POISE_DEBUG') unless defined?(@debug_file_upper) + @debug_file_lower = ::File.exist?('/poise_debug') unless defined?(@debug_file_lower) + !!( + (ENV['POISE_DEBUG'] && ENV['POISE_DEBUG'] != 'false') || + (ENV['poise_debug'] && ENV['poise_debug'] != 'false') || + (node && node['POISE_DEBUG']) || + (node && node['poise_debug']) || + @debug_file_upper || + @debug_file_lower + ) + end + + # Log a message only if Poise's extra debugging output is enabled. + # + # @see #debug? + # @param msg [String] Log message. + # @return [void] + def self.debug(msg) + Chef::Log.debug(msg) if debug? + end +end + +# Callable form to allow passing in options: +# include Poise(ParentResource) +# include Poise(parent: ParentResource) +# include Poise(container: true) +def Poise(options={}) + # Allow passing a class as a shortcut + if options.is_a?(Class) + options = {parent: options} + end + + # Create a new anonymous module + mod = Module.new + + # Fake the name. + mod.define_singleton_method(:name) do + super() || 'Poise' + end + + mod.define_singleton_method(:included) do |klass| + super(klass) + # Pull in the main helper to cover most of the needed logic. + klass.class_exec { include Poise } + # Set the defined_in values as needed. + klass.poise_defined!(caller) + # Resource-specific options. + if klass < Chef::Resource + klass.poise_subresource(options[:parent], options[:parent_optional], options[:parent_auto]) if options[:parent] + klass.poise_subresource_container(options[:container_namespace], options[:container_default]) if options[:container] + klass.poise_fused if options[:fused] + klass.poise_inversion(options[:inversion_options_resource]) if options[:inversion] + end + # Provider-specific options. + if klass < Chef::Provider + klass.poise_inversion(options[:inversion], options[:inversion_attribute]) if options[:inversion] + end + end + + mod +end + +# Display a message if poise_debug is enabled. Off in ChefSpec so I don't get +# extra logging stuff that I don't care about. +Poise.debug('[Poise] Extra verbose logging enabled') unless defined?(ChefSpec) diff --git a/cookbooks/poise/files/halite_gem/poise/backports.rb b/cookbooks/poise/files/halite_gem/poise/backports.rb new file mode 100644 index 0000000..84ceed8 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/backports.rb @@ -0,0 +1,28 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + # Backported features from Chef to be able to use them with older versions. + # + # @since 2.3.0 + module Backports + autoload :NOT_PASSED, 'poise/backports/not_passed' + autoload :VERIFY_PATH, 'poise/backports/verify_path' + end + + autoload :NOT_PASSED, 'poise/backports/not_passed' +end diff --git a/cookbooks/poise/files/halite_gem/poise/backports/not_passed.rb b/cookbooks/poise/files/halite_gem/poise/backports/not_passed.rb new file mode 100644 index 0000000..c5b3ce0 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/backports/not_passed.rb @@ -0,0 +1,52 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +begin + require 'chef/constants' +rescue LoadError + # This space left intentionally blank. +end + + +module Poise + module Backports + # A sentinel value for optional arguments where nil is a valid value. + # @since 2.3.0 + # @!parse NOT_PASSED = Object.new + NOT_PASSED = if defined?(Chef::NOT_PASSED) + Chef::NOT_PASSED + else + # Copyright 2015-2016, Chef Software Inc. + # Used under Apache License, Version 2.0. + Object.new.tap do |not_passed| + def not_passed.to_s + "NOT_PASSED" + end + def not_passed.inspect + to_s + end + not_passed.freeze + end + end + + end + + # An alias to {Backports::NOT_PASSED} to avoid typing so much. + # + # @since 2.3.0 + # @see Backports::NOT_PASSED + NOT_PASSED = Backports::NOT_PASSED +end diff --git a/cookbooks/poise/files/halite_gem/poise/backports/verify_path.rb b/cookbooks/poise/files/halite_gem/poise/backports/verify_path.rb new file mode 100644 index 0000000..8bd4c06 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/backports/verify_path.rb @@ -0,0 +1,33 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + module Backports + # The correct interpolation key for any version of Chef. + # @since 2.6.0 + # @example + # file '/path' do + # content my_content + # verify "myapp -t #{Poise::Backports::VERIFY_PATH}" + # end + VERIFY_PATH = if Gem::Version.create(Chef::VERSION) < Gem::Version.create('12.5.0') + '%{file}' + else + '%{path}' + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/error.rb b/cookbooks/poise/files/halite_gem/poise/error.rb new file mode 100644 index 0000000..9a6a948 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/error.rb @@ -0,0 +1,24 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + # Base exception class for Poise errors. + # + # @since 2.0.0 + class Error < Exception + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers.rb b/cookbooks/poise/files/halite_gem/poise/helpers.rb new file mode 100644 index 0000000..ab2fc33 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers.rb @@ -0,0 +1,36 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + module Helpers + autoload :ChefspecMatchers, 'poise/helpers/chefspec_matchers' + autoload :DefinedIn, 'poise/helpers/defined_in' + autoload :Fused, 'poise/helpers/fused' + autoload :IncludeRecipe, 'poise/helpers/include_recipe' + autoload :Inversion, 'poise/helpers/inversion' + autoload :LazyDefault, 'poise/helpers/lazy_default' + autoload :LWRPPolyfill, 'poise/helpers/lwrp_polyfill' + autoload :NotifyingBlock, 'poise/helpers/notifying_block' + autoload :OptionCollector, 'poise/helpers/option_collector' + autoload :ResourceCloning, 'poise/helpers/resource_cloning' + autoload :ResourceName, 'poise/helpers/resource_name' + autoload :ResourceSubclass, 'poise/helpers/resource_subclass' + autoload :Subresources, 'poise/helpers/subresources' + autoload :TemplateContent, 'poise/helpers/template_content' + autoload :Win32User, 'poise/helpers/win32_user' + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/chefspec_matchers.rb b/cookbooks/poise/files/halite_gem/poise/helpers/chefspec_matchers.rb new file mode 100644 index 0000000..7417628 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/chefspec_matchers.rb @@ -0,0 +1,92 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Not requiring chefspec or rspec/expectations since this code should only +# activate if they are already loaded. + +require 'poise/helpers/lwrp_polyfill' +require 'poise/helpers/resource_name' + + +module Poise + module Helpers + # A resource mixin to register ChefSpec matchers for a resource + # automatically. + # + # If you are using the provides() form for naming resources, ensure that is + # set before declaring actions. + # + # @since 2.0.0 + # @example Define a class + # class Chef::Resource::MyResource < Chef::Resource + # include Poise::Helpers::ChefspecMatchers + # actions(:run) + # end + # @example Use a matcher + # expect(chef_run).to run_my_resource('...') + module ChefspecMatchers + include Poise::Helpers::LWRPPolyfill::Resource + include Poise::Helpers::ResourceName + + # Create a matcher for a given resource type and action. This is + # idempotent so if a matcher already exists, it will not be recreated. + # + # @api private + def self.create_matcher(resource, action) + # Check that we have everything we need. + return unless defined?(ChefSpec) && defined?(RSpec::Matchers) && resource + method = :"#{action}_#{resource}" + return if RSpec::Matchers.method_defined?(method) + RSpec::Matchers.send(:define_method, method) do |resource_name| + ChefSpec::Matchers::ResourceMatcher.new(resource, action, resource_name) + end + end + + # @!classmethods + module ClassMethods + # Create a resource-level matcher for this resource. + # + # @see Resource::ResourceName.provides + def provides(name, *args, &block) + super(name, *args, &block) + ChefSpec.define_matcher(name) if defined?(ChefSpec) + # Call #actions here to grab any actions from a parent class. + actions.each do |action| + ChefspecMatchers.create_matcher(name, action) + end + end + + # Create matchers for all declared actions. + # + # @see Resource::LWRPPolyfill.actions + def actions(*names) + super.tap do |actions| + actions.each do |action| + ChefspecMatchers.create_matcher(resource_name, action) + end if resource_name && resource_name != :resource && !names.empty? + end + end + + def included(klass) + super + klass.extend ClassMethods + end + end + + extend ClassMethods + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/defined_in.rb b/cookbooks/poise/files/halite_gem/poise/helpers/defined_in.rb new file mode 100644 index 0000000..43944ba --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/defined_in.rb @@ -0,0 +1,128 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/error' +require 'poise/utils' + + +module Poise + module Helpers + # A mixin to track where a resource or provider was defined. This can + # provide either the filename of the class or the cookbook it was defined in. + # + # @since 2.0.0 + # @example + # class MyProvider < Chef::provider + # include Poise::Helpers::DefinedIn + # + # def action_create + # template '...' do + # # ... + # cookbook new_resource.poise_defined_in + # end + # end + # end + module DefinedIn + # Path to the root of Poise's code. + # @see #poise_defined! + # @api private + POISE_LIB_ROOT = ::File.expand_path('../..', __FILE__) + + # Path to the root of Chef's code. + # @see #poise_defined! + # @api private + CHEF_LIB_ROOT = ::File.join(::Gem::Specification.find_by_name('chef').gem_dir, 'lib') + + # A regex used to parse Ruby's `caller` string syntax. + # @see #poise_defined! + # @api private + CALLER_REGEXP = /^(.+):\d+:in `.+'/ + + # Wrapper for {.poise_defined_in_cookbook} to pass the run context for you. + # + # @see .poise_defined_in_cookbook + # @param file [String, nil] Optional file path to check instead of the path + # this class was defined in. + # @return [String] + def poise_defined_in_cookbook(file=nil) + self.class.poise_defined_in_cookbook(run_context, file) + end + + # @!classmethods + module ClassMethods + # The file this class or module was defined in, or nil if it isn't found. + # + # @return [String] + def poise_defined_in + raise Poise::Error.new("Unable to determine location of #{self.name}") unless @poise_defined_in + @poise_defined_in + end + + # The cookbook this class or module was defined in. Can pass a file to + # check that instead. + # + # @param run_context [Chef::RunContext] Run context to check cookbooks in. + # @param file [String, nil] Optional file path to check instead of the + # path this class was defined in. + # @return [String] + def poise_defined_in_cookbook(run_context, file=nil) + file ||= poise_defined_in + Poise.debug("[#{self.name}] Checking cookbook name for #{file}") + Poise::Utils.find_cookbook_name(run_context, file).tap do |cookbook| + Poise.debug("[#{self.name}] found cookbook #{cookbook.inspect}") + end + end + + # Record that the class/module was defined. Called automatically by Ruby + # for all normal cases. + # + # @param caller_array [Array] A strack trace returned by #caller. + # @return [void] + def poise_defined!(caller_array) + # Only try to set this once. + return if @poise_defined_in + # Parse out just the filenames. + caller_paths = caller_array.map {|line| line[CALLER_REGEXP, 1] } + # Find the first non-poise, non-chef line. This assumes Halite + # transformation which I'm not thrilled about. + caller_path = caller_paths.find do |line| + line && !line.start_with?(POISE_LIB_ROOT) && !line.start_with?(CHEF_LIB_ROOT) + end + raise Poise::Error.new("Unable to find a caller path for: #{caller_array.inspect}") unless caller_path + if ::File::ALT_SEPARATOR + caller_path.gsub!(::File::ALT_SEPARATOR, ::File::SEPARATOR) + end + Chef::Log.debug("[#{self.name}] Recording poise_defined_in as #{caller_path}") + @poise_defined_in = caller_path + end + + # @api private + def inherited(klass) + super + klass.poise_defined!(caller) + end + + def included(klass) + super + klass.extend(ClassMethods) + klass.poise_defined!(caller) + end + end + + extend ClassMethods + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/fused.rb b/cookbooks/poise/files/halite_gem/poise/helpers/fused.rb new file mode 100644 index 0000000..8b67bea --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/fused.rb @@ -0,0 +1,127 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider' + + +module Poise + module Helpers + # Resource mixin to create "fused" resources where the resource and provider + # are implemented in the same class. + # + # @since 2.0.0 + # @example + # class Chef::Resource::MyResource < Chef::Resource + # include Poise(fused: true) + # attribute(:path, kind_of: String) + # attribute(:message, kind_of: String) + # action(:run) do + # file new_resource.path do + # content new_resource.message + # end + # end + # end + module Fused + # Hack is_a? so that the DSL will consider this a Provider for the + # purposes of attaching enclosing_provider. + # + # @api private + # @param klass [Class] + # @return [Boolean] + def is_a?(klass) + if klass == Chef::Provider + # Lies, damn lies, and Ruby code. + true + else + super + end + end + + # Hack provider_for_action so that the resource is also the provider. + # + # @api private + # @param action [Symbol] + # @return [Chef::Provider] + def provider_for_action(action) + provider(self.class.fused_provider_class) unless provider + super + end + + # @!classmethods + module ClassMethods + # Define a provider action. The block should contain the usual provider + # code. + # + # @param name [Symbol] Name of the action. + # @param block [Proc] Action implementation. + # @example + # action(:run) do + # file '/temp' do + # user 'root' + # content 'temp' + # end + # end + def action(name, &block) + fused_actions[name.to_sym] = block + # Make sure this action is allowed, also sets the default if first. + if respond_to?(:actions) + actions(name.to_sym) + end + end + + # Storage accessor for fused action blocks. Maps action name to proc. + # + # @api private + # @return [Hash] + def fused_actions + (@fused_actions ||= {}) + end + + # Create a provider class for the fused actions in this resource. + # Inherits from the fused provider class of the resource's superclass if + # present. + # + # @api private + # @return [Class] + def fused_provider_class + @fused_provider_class ||= begin + provider_superclass = begin + self.superclass.fused_provider_class + rescue NoMethodError + Chef::Provider + end + actions = fused_actions + class_name = self.name + Class.new(provider_superclass) do + include Poise + define_singleton_method(:name) { class_name + ' (fused)' } + actions.each do |action, block| + define_method(:"action_#{action}", &block) + end + end + end + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/include_recipe.rb b/cookbooks/poise/files/halite_gem/poise/helpers/include_recipe.rb new file mode 100644 index 0000000..2b84e30 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/include_recipe.rb @@ -0,0 +1,62 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/helpers/subcontext_block' +require 'poise/subcontext/runner' + + +module Poise + module Helpers + # A provider mixin to add #include_recipe that can be called from action + # methods. + # + # @since 2.0.0 + module IncludeRecipe + include Poise::Helpers::SubcontextBlock + + def include_recipe(*recipes) + loaded_recipes = [] + subcontext = subcontext_block do + recipes.flatten.each do |recipe| + case recipe + when String + # Process normally + Chef::Log.debug("Loading recipe #{recipe} via include_recipe (poise)") + loaded_recipes += run_context.include_recipe(recipe) + when Proc + # Pretend its a block of recipe code + fake_recipe = Chef::Recipe.new(cookbook_name, new_resource.recipe_name, run_context) + fake_recipe.instance_eval(&recipe) + loaded_recipes << fake_recipe + end + end + end + # Converge the new context. + Poise::Subcontext::Runner.new(new_resource, subcontext).converge + collection = global_resource_collection + subcontext.resource_collection.each do |r| + Chef::Log.debug("Poise::IncludeRecipe: Adding #{r} to global collection #{collection.object_id}") + # Insert the local resource into the global context + collection.insert(r) + # Skip the iterator forward so we don't double-execute the inserted resource + # If running at compile time, the iterator is nil + collection.iterator.skip_forward if collection.iterator + end + loaded_recipes + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/inversion.rb b/cookbooks/poise/files/halite_gem/poise/helpers/inversion.rb new file mode 100644 index 0000000..76b64ba --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/inversion.rb @@ -0,0 +1,414 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/node' +require 'chef/node_map' +require 'chef/provider' +require 'chef/resource' + +require 'poise/backports' +require 'poise/helpers/defined_in' +require 'poise/error' +require 'poise/helpers/inversion/options_resource' +require 'poise/utils/resource_provider_mixin' + + +module Poise + module Helpers + # A mixin for dependency inversion in Chef. + # + # @since 2.0.0 + module Inversion + autoload :OptionsResource, 'poise/helpers/inversion/options_resource' + autoload :OptionsProvider, 'poise/helpers/inversion/options_provider' + + include Poise::Utils::ResourceProviderMixin + + # Resource implementation for {Poise::Helpers::Inversion}. + # @see Poise::Helpers::Inversion + module Resource + # @overload options(val=nil) + # Set or return provider options for all providers. + # @param val [Hash] Provider options to set. + # @return [Hash] + # @example + # my_resource 'thing_one' do + # options depends: 'thing_two' + # end + # @overload options(provider, val=nil) + # Set or return provider options for a specific provider. + # @param provider [Symbol] Provider to set for. + # @param val [Hash] Provider options to set. + # @return [Hash] + # @example + # my_resource 'thing_one' do + # options :my_provider, depends: 'thing_two' + # end + def options(provider=nil, val=nil) + key = :options + if !val && provider.is_a?(Hash) + val = provider + elsif provider + key = :"options_#{provider}" + end + set_or_return(key, val ? Mash.new(val) : val, kind_of: Hash, default: lazy { Mash.new }) + end + + # Allow setting the provider directly using the same names as the attribute + # settings. + # + # @param val [String, Symbol, Class, nil] Value to set the provider to. + # @return [Class] + # @example + # my_resource 'thing_one' do + # provider :my_provider + # end + def provider(val=nil) + if val && !val.is_a?(Class) + resource_names = [resource_name] + # If subclass_providers! might be in play, check for those names too. + resource_names.concat(self.class.subclass_resource_equivalents) if self.class.respond_to?(:subclass_resource_equivalents) + # Silly ruby tricks to find the first provider that exists and no more. + provider_class = resource_names.lazy.map {|name| Poise::Helpers::Inversion.provider_for(name, node, val) }.select {|x| x }.first + Poise.debug("[#{self}] Checking for an inversion provider for #{val}: #{provider_class && provider_class.name}") + val = provider_class if provider_class + end + super + end + + # Set or return the array of provider names to be blocked from + # auto-resolution. + # + # @param val [String, Array] Value to set. + # @return [Array] + def provider_no_auto(val=nil) + # Coerce to an array. + val = Array(val).map(&:to_s) if val + set_or_return(:provider_no_auto, val, kind_of: Array, default: []) + end + + # @!classmethods + module ClassMethods + # Options resource class. + attr_reader :inversion_options_resource_class + # Options provider class. + attr_reader :inversion_options_provider_class + + # @overload inversion_options_resource() + # Return the options resource mode for this class. + # @return [Boolean] + # @overload inversion_options_resource(val) + # Set the options resource mode for this class. Set to true to + # automatically create an options resource. Defaults to true. + # @param val [Boolean] Enable/disable setting. + # @return [Boolean] + def inversion_options_resource(val=nil) + @poise_inversion_options_resource = val unless val.nil? + @poise_inversion_options_resource + end + + # Create resource and provider classes for an options resource. + # + # @param name [String, Symbol] DSL name for the base resource. + # @return [void] + def create_inversion_options_resource!(name) + enclosing_class = self + options_resource_name = :"#{name}_options" + # Create the resource class. + @inversion_options_resource_class = Class.new(Chef::Resource) do + include Poise::Helpers::Inversion::OptionsResource + define_singleton_method(:name) do + "#{enclosing_class}::OptionsResource" + end + define_singleton_method(:inversion_resource_class) do + enclosing_class + end + provides(options_resource_name) + inversion_resource(name) + end + # Create the provider class. + @inversion_options_provider_class = Class.new(Chef::Provider) do + include Poise::Helpers::Inversion::OptionsProvider + define_singleton_method(:name) do + "#{enclosing_class}::OptionsProvider" + end + define_singleton_method(:inversion_resource_class) do + enclosing_class + end + provides(options_resource_name) + end + end + + # Wrap #provides() to create an options resource if desired. + # + # @param name [Symbol] Resource name + # return [void] + def provides(name, *args, &block) + create_inversion_options_resource!(name) if inversion_options_resource + super(name, *args, &block) if defined?(super) + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end + + # Provider implementation for {Poise::Helpers::Inversion}. + # @see Poise::Helpers::Inversion + module Provider + include DefinedIn + + # Compile all the different levels of inversion options together. + # + # @return [Hash] + # @example + # def action_run + # if options['depends'] + # # ... + # end + # end + def options + @options ||= self.class.inversion_options(node, new_resource) + end + + # @!classmethods + module ClassMethods + # @overload inversion_resource() + # Return the inversion resource name for this class. + # @return [Symbo, nill] + # @overload inversion_resource(val) + # Set the inversion resource name for this class. You can pass either + # a symbol in DSL format or a resource class that uses Poise. This + # name is used to determine which resources the inversion provider is + # a candidate for. + # @param val [Symbol, Class] Name to set. + # @return [Symbol, nil] + def inversion_resource(val=Poise::NOT_PASSED) + if val != Poise::NOT_PASSED + val = val.resource_name if val.is_a?(Class) + Chef::Log.debug("[#{self.name}] Setting inversion resource to #{val}") + @poise_inversion_resource = val.to_sym + end + if defined?(@poise_inversion_resource) + @poise_inversion_resource + else + Poise::Utils.ancestor_send(self, :inversion_resource, default: nil) + end + end + + # @overload inversion_attribute() + # Return the inversion attribute name(s) for this class. + # @return [Array, nil] + # @overload inversion_attribute(val) + # Set the inversion attribute name(s) for this class. This is + # used by {.resolve_inversion_attribute} to load configuration data + # from node attributes. To specify a nested attribute pass an array + # of strings corresponding to the keys. + # @param val [String, Array] Attribute path. + # @return [Array, nil] + def inversion_attribute(val=Poise::NOT_PASSED) + if val != Poise::NOT_PASSED + # Coerce to an array of strings. + val = Array(val).map {|name| name.to_s } + @poise_inversion_attribute = val + end + if defined?(@poise_inversion_attribute) + @poise_inversion_attribute + else + Poise::Utils.ancestor_send(self, :inversion_attribute, default: nil) + end + end + + # Default attribute paths to check for inversion options. Based on + # the cookbook this class and its superclasses are defined in. + # + # @param node [Chef::Node] Node to load from. + # @return [Array>] + def default_inversion_attributes(node) + klass = self + tried = [] + while klass.respond_to?(:poise_defined_in_cookbook) + cookbook = klass.poise_defined_in_cookbook(node.run_context) + if node[cookbook] + return [cookbook] + end + tried << cookbook + klass = klass.superclass + end + raise Poise::Error.new("Unable to find inversion attributes, tried: #{tried.join(', ')}") + end + + # Resolve the node attribute used as the base for inversion options + # for this class. This can be set explicitly with {.inversion_attribute} + # or the default is to use the name of the cookbook the provider is + # defined in. + # + # @param node [Chef::Node] Node to load from. + # @return [Chef::Node::Attribute] + def resolve_inversion_attribute(node) + # Default to using just the name of the cookbook. + attribute_names = inversion_attribute || default_inversion_attributes(node) + return {} if attribute_names.empty? + attribute_names.inject(node) do |memo, key| + memo[key] || begin + raise Poise::Error.new("Attribute #{key} not set when expanding inversion attribute for #{self.name}: #{memo}") + end + end + end + + # Compile all the different levels of inversion options together. + # + # @param node [Chef::Node] Node to load from. + # @param resource [Chef::Resource] Resource to load from. + # @return [Hash] + def inversion_options(node, resource) + Mash.new.tap do |opts| + attrs = resolve_inversion_attribute(node) + # Cast the run state to a Mash because string vs. symbol keys. I can + # at least promise poise_inversion will be a str so cut down on the + # amount of data to convert. + run_state = Mash.new(node.run_state.fetch('poise_inversion', {}).fetch(inversion_resource, {}))[resource.name] || {} + # Class-level defaults. + opts.update(default_inversion_options(node, resource)) + # Resource options for all providers. + opts.update(resource.options) if resource.respond_to?(:options) + # Global provider from node attributes. + opts.update(provider: attrs['provider']) if attrs['provider'] + # Attribute options for all providers. + opts.update(attrs['options']) if attrs['options'] + # Resource options for this provider. + opts.update(resource.options(provides)) if resource.respond_to?(:options) + # Attribute options for this resource name. + opts.update(attrs[resource.name]) if attrs[resource.name] + # Options resource options for all providers. + opts.update(run_state['*']) if run_state['*'] + # Options resource options for this provider. + opts.update(run_state[provides]) if run_state[provides] + # Vomitdebug output for tracking down weirdness. + Poise.debug("[#{resource}] Resolved inversion options: #{opts.inspect}") + end + end + + # Default options data for this provider class. + # + # @param node [Chef::Node] Node to load from. + # @param resource [Chef::Resource] Resource to load from. + # @return [Hash] + def default_inversion_options(node, resource) + {} + end + + # Resolve which provider name should be used for a resource. + # + # @param node [Chef::Node] Node to load from. + # @param resource [Chef::Resource] Resource to query. + # @return [String] + def resolve_inversion_provider(node, resource) + inversion_options(node, resource)['provider'] || 'auto' + end + + # Override the normal #provides to set the inversion provider name + # instead of adding to the normal provider map. + # + # @overload provides() + # Return the inversion provider name for the class. + # @return [Symbol] + # @overload provides(name, opts={}, &block) + # Set the inversion provider name for the class. + # @param name [Symbol] Provider name. + # @param opts [Hash] NodeMap filter options. + # @param block [Proc] NodeMap filter proc. + # @return [Symbol] + def provides(name=nil, opts={}, &block) + if name + raise Poise::Error.new("Inversion resource name not set for #{self.name}") unless inversion_resource + @poise_inversion_provider = name + Chef::Log.debug("[#{self.name}] Setting inversion provider name to #{name}") + Poise::Helpers::Inversion.provider_map(inversion_resource).set(name.to_sym, self, opts, &block) + # Set the actual Chef-level provides name for DSL dispatch. + super(inversion_resource) + end + @poise_inversion_provider + end + + # Override the default #provides? to check for our inverted providers. + # + # @api private + # @param node [Chef::Node] Node to use for attribute checks. + # @param resource [Chef::Resource] Resource instance to match. + # @return [Boolean] + def provides?(node, resource) + raise Poise::Error.new("Inversion resource name not set for #{self.name}") unless inversion_resource + resource_name_equivalents = {resource.resource_name => true} + # If subclass_providers! might be in play, check for those names too. + if resource.class.respond_to?(:subclass_resource_equivalents) + resource.class.subclass_resource_equivalents.each do |name| + resource_name_equivalents[name] = true + end + end + return false unless resource_name_equivalents[inversion_resource] + provider_name = resolve_inversion_provider(node, resource).to_s + Poise.debug("[#{resource}] Checking provides? on #{self.name}. Got provider_name #{provider_name.inspect}") + provider_name == provides.to_s || ( provider_name == 'auto' && !resource.provider_no_auto.include?(provides.to_s) && provides_auto?(node, resource) ) + end + + # Subclass hook to provide auto-detection for providers. + # + # @param node [Chef::Node] Node to check against. + # @param resource [Chef::Resource] Resource to check against. + # @return [Boolean] + def provides_auto?(node, resource) + false + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end + + # The provider map for a given resource type. + # + # @param resource_type [Symbol] Resource type in DSL format. + # @return [Chef::NodeMap] + # @example + # Poise::Helpers::Inversion.provider_map(:my_resource) + def self.provider_map(resource_type) + @provider_maps ||= {} + @provider_maps[resource_type.to_sym] ||= Chef::NodeMap.new + end + + # Find a specific provider class for a resource. + # + # @param resource_type [Symbol] Resource type in DSL format. + # @param node [Chef::Node] Node to use for the lookup. + # @param provider_type [Symbol] Provider type in DSL format. + # @return [Class] + # @example + # Poise::Helpers::Inversion.provider_for(:my_resource, node, :my_provider) + def self.provider_for(resource_type, node, provider_type) + provider_map(resource_type).get(node, provider_type.to_sym) + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/inversion/options_provider.rb b/cookbooks/poise/files/halite_gem/poise/helpers/inversion/options_provider.rb new file mode 100644 index 0000000..9ea5f94 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/inversion/options_provider.rb @@ -0,0 +1,41 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + module Helpers + module Inversion + # A mixin for inversion options providers. + # + # @api private + # @since 2.0.0 + # @see Poise::Helper::Inversion + module OptionsProvider + # @api private + def self.included(klass) + klass.class_exec { include Poise } + end + + # A blank run action. + # + # @return [void] + def action_run + # This space left intentionally blank. + end + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/inversion/options_resource.rb b/cookbooks/poise/files/halite_gem/poise/helpers/inversion/options_resource.rb new file mode 100644 index 0000000..9ff47a7 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/inversion/options_resource.rb @@ -0,0 +1,115 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mash' + +require 'poise/backports' +require 'poise/error' + + +module Poise + module Helpers + module Inversion + # A mixin for inversion options resources. + # + # @api private + # @since 2.0.0 + # @see Poise::Helpers::Inversion + module OptionsResource + include Poise + + # Method missing delegation to allow DSL-style options. + # + # @example + # my_app_options 'app' do + # key1 'value1' + # key2 'value2' + # end + def method_missing(method_sym, *args, &block) + super(method_sym, *args, &block) + rescue NoMethodError + # First time we've seen this key and using it as an rvalue, NOPE.GIF. + raise unless !args.empty? || block || _options[method_sym] + if !args.empty? || block + _options[method_sym] = block || args.first + end + _options[method_sym] + end + + # Capture setting the provider and make it Do What I Mean. This does + # mean you can't set the actual provider for the options resource, which + # is fine because the provider is a no-op. + # + # @api private + def provider(val=Poise::NOT_PASSED) + if val == Poise::NOT_PASSED + super() + else + _options[:provider] = val + end + end + + # Insert the options data in to the run state. This has to match the + # layout used in {Poise::Helpers::Inversion::Provider.inversion_options}. + # + # @api private + def after_created + raise Poise::Error.new("Inversion resource name not set for #{self.class.name}") unless self.class.inversion_resource + node.run_state['poise_inversion'] ||= {} + node.run_state['poise_inversion'][self.class.inversion_resource] ||= {} + node.run_state['poise_inversion'][self.class.inversion_resource][resource] ||= {} + node.run_state['poise_inversion'][self.class.inversion_resource][resource][for_provider] ||= {} + node.run_state['poise_inversion'][self.class.inversion_resource][resource][for_provider].update(_options) + end + + module ClassMethods + # @overload inversion_resource() + # Return the inversion resource name for this class. + # @return [Symbol] + # @overload inversion_resource(val) + # Set the inversion resource name for this class. You can pass either + # a symbol in DSL format or a resource class that uses Poise. This + # name is used to determine which resources the inversion provider is + # a candidate for. + # @param val [Symbol, Class] Name to set. + # @return [Symbol] + def inversion_resource(val=nil) + if val + val = val.resource_name if val.is_a?(Class) + Chef::Log.debug("[#{self.name}] Setting inversion resource to #{val}") + @poise_inversion_resource = val.to_sym + end + @poise_inversion_resource || (superclass.respond_to?(:inversion_resource) ? superclass.inversion_resource : nil) + end + + # @api private + def included(klass) + super + klass.extend(ClassMethods) + klass.class_exec do + actions(:run) + attribute(:resource, kind_of: String, name_attribute: true) + attribute(:for_provider, kind_of: [String, Symbol], default: '*') + attribute(:_options, kind_of: Hash, default: lazy { Mash.new }) + end + end + end + + extend ClassMethods + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/lazy_default.rb b/cookbooks/poise/files/halite_gem/poise/helpers/lazy_default.rb new file mode 100644 index 0000000..59a04c9 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/lazy_default.rb @@ -0,0 +1,79 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/version' + + +module Poise + module Helpers + # Resource mixin to allow lazyily-evaluated defaults in resource attributes. + # This is designed to be used with {LWRPPolyfill} or a similar #attributes + # method. + # + # @since 1.0.0 + # @example + # class MyResource < Chef::Resource + # include Poise::Helpers::LWRPPolyfill + # include Poise::Helpers::LazyDefault + # attribute(:path, default: lazy { name + '_temp' }) + # end + module LazyDefault + # Check if this version of Chef already supports lazy defaults. This is + # true for Chef 12.5+. + # + # @since 2.0.3 + # @api private + # @return [Boolean] + def self.needs_polyfill? + @needs_polyfill ||= Gem::Requirement.new('< 12.5.pre').satisfied_by?(Gem::Version.new(Chef::VERSION)) + end + + # Override the default set_or_return to support lazy evaluation of the + # default value. This only actually matters when it is called from a class + # level context via #attributes. + def set_or_return(symbol, arg, validation) + if LazyDefault.needs_polyfill? && validation && validation[:default].is_a?(Chef::DelayedEvaluator) + validation = validation.dup + if (arg.nil? || arg == Poise::NOT_PASSED) && (!instance_variable_defined?(:"@#{symbol}") || instance_variable_get(:"@#{symbol}").nil?) + validation[:default] = instance_eval(&validation[:default]) + else + # Clear the default. + validation.delete(:default) + end + end + super(symbol, arg, validation) + end + + # @!classmethods + module ClassMethods + # Create a lazyily-evaluated block. + # + # @param block [Proc] Callable to return the default value. + # @return [Chef::DelayedEvaluator] + def lazy(&block) + Chef::DelayedEvaluator.new(&block) + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/lwrp_polyfill.rb b/cookbooks/poise/files/halite_gem/poise/helpers/lwrp_polyfill.rb new file mode 100644 index 0000000..a8c2541 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/lwrp_polyfill.rb @@ -0,0 +1,163 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' + +require 'poise/utils/resource_provider_mixin' + + +module Poise + module Helpers + # A resource and provider mixin to add back some compatability with Chef's + # LWRPBase classes. + # + # @since 1.0.0 + module LWRPPolyfill + include Poise::Utils::ResourceProviderMixin + + # Provide default_action and actions like LWRPBase but better equipped for subclassing. + module Resource + def initialize(*args) + super + # Try to not stomp on stuff if already set in a parent. Coerce @action + # to an array because this behavior may change in the future in Chef. + @action = self.class.default_action if Array(@action) == [:nothing] + (@allowed_actions << self.class.actions).flatten!.uniq! + end + + module ClassMethods + # @overload default_action() + # Get the default action for this resource class. If no explicit + # default is set, the first action in the list will be used. + # @see #actions + # @return [Array] + # @overload default_action(name) + # Set the default action for this resource class. If this action is + # not already allowed, it will be added. + # @note It is idiomatic to use {#actions} instead, with the first + # action specified being the default. + # @param name [Symbol, Array] Name of the action(s). + # @return [Array] + # @example + # class MyApp < Chef::Resource + # include Poise + # default_action(:install) + # end + def default_action(name=nil) + if name + name = Array(name).flatten.map(&:to_sym) + @default_action = name + actions(*name) + end + if @default_action + @default_action + elsif respond_to?(:superclass) && superclass != Chef::Resource && superclass.respond_to?(:default_action) && superclass.default_action && Array(superclass.default_action) != %i{nothing} + superclass.default_action + elsif first_non_nothing = actions.find {|action| action != :nothing } + [first_non_nothing] + else + %i{nothing} + end + end + + # @overload actions() + # Get all actions allowed for this resource class. This includes + # any actions allowed on parent classes. + # @return [Array] + # @overload actions(*names) + # Set actions as allowed for this resource class. These must + # correspond with action methods in the provider class(es). + # @param names [Array] One or more actions to set. + # @return [Array] + # @example + # class MyApp < Chef::Resource + # include Poise + # actions(:install, :uninstall) + # end + def actions(*names) + @actions ||= ( respond_to?(:superclass) && superclass.respond_to?(:actions) && superclass.actions.dup ) || ( respond_to?(:superclass) && superclass != Chef::Resource && superclass.respond_to?(:allowed_actions) && superclass.allowed_actions.dup ) || [] + (@actions << names).tap {|actions| actions.flatten!; actions.uniq! } + end + + # Create a resource property (née attribute) on this resource class. + # This follows the same usage as the helper of the same name in Chef + # LWRPs. + # + # @param name [Symbol] Name of the property. + # @param opts [Hash] Validation options and flags. + # @return [void] + # @example + # class MyApp < Chef::Resource + # include Poise + # attribute(:path, name_attribute: true) + # attribute(:port, kind_of: Integer, default: 8080) + # end + def attribute(name, opts={}) + # Freeze the default value. This is done upstream too in Chef 12.5+. + opts[:default].freeze if opts && opts[:default] + # Ruby 1.8 can go to hell. + define_method(name) do |arg=nil, &block| + arg = block if arg.nil? # Try to allow passing either. + set_or_return(name, arg, opts) + end + end + + # For forward compat with Chef 12.5+. + alias_method :property, :attribute + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end + + # Helper to handle load_current_resource for direct subclasses of Provider + module Provider + module LoadCurrentResource + def load_current_resource + @current_resource = if new_resource + new_resource.class.new(new_resource.name, run_context) + else + # Better than nothing, subclass can overwrite anyway. + Chef::Resource.new(nil, run_context) + end + end + end + + # @!classmethods + module ClassMethods + def included(klass) + super + klass.extend(ClassMethods) + + # Mask Chef::Provider#load_current_resource because it throws NotImplementedError. + if klass.is_a?(Class) && klass.superclass == Chef::Provider + klass.send(:include, LoadCurrentResource) + end + + # Reinstate the Chef DSL, removed in Chef 12. + klass.send(:include, Chef::DSL::Recipe) + end + end + + extend ClassMethods + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/notifying_block.rb b/cookbooks/poise/files/halite_gem/poise/helpers/notifying_block.rb new file mode 100644 index 0000000..72708c9 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/notifying_block.rb @@ -0,0 +1,78 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/helpers/subcontext_block' +require 'poise/subcontext/runner' + + +module Poise + module Helpers + # A provider mixin to provide #notifying_block, a scoped form of Chef's + # use_inline_resources. + # + # @since 1.0.0 + # @example + # class MyProvider < Chef::Provider + # include Chef::Helpers::NotifyingBlock + # + # def action_run + # notifying_block do + # template '/etc/myapp.conf' do + # # ... + # end + # end + # end + # end + module NotifyingBlock + include Poise::Helpers::SubcontextBlock + + private + + # Create and converge a subcontext for the recipe DSL. This is similar to + # Chef's use_inline_resources but is scoped to a block. All DSL resources + # declared inside the block will be converged when the block returns, and + # the updated_by_last_action flag will be set if any of the inner + # resources are updated. + # + # @api public + # @param block [Proc] Block to run in the subcontext. + # @return [void] + # @example + # def action_run + # notifying_block do + # template '/etc/myapp.conf' do + # # ... + # end + # end + # end + def notifying_block(&block) + # Make sure to mark the resource as updated-by-last-action if + # any sub-run-context resources were updated (any actual + # actions taken against the system) during the + # sub-run-context convergence. + begin + subcontext = subcontext_block(&block) + # Converge the new context. + Poise::Subcontext::Runner.new(new_resource, subcontext).converge + ensure + new_resource.updated_by_last_action( + subcontext && subcontext.resource_collection.any?(&:updated?) + ) + end + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/option_collector.rb b/cookbooks/poise/files/halite_gem/poise/helpers/option_collector.rb new file mode 100644 index 0000000..ef48a42 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/option_collector.rb @@ -0,0 +1,142 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mash' + +require 'poise/error' + + +module Poise + module Helpers + # A resource mixin to add a new kind of attribute, an option collector. + # These attributes can act as mini-DSLs for things which would otherwise be + # key/value pairs. + # + # @since 1.0.0 + # @example Defining an option collector + # class MyResource < Chef::Resource + # include Poise::Helpers::OptionCollector + # attribute(:my_options, option_collector: true) + # end + # @example Using an option collector + # my_resource 'name' do + # my_options do + # key1 'value1' + # key2 'value2' + # end + # end + module OptionCollector + # Instance context used to eval option blocks. + # @api private + class OptionEvalContext + attr_reader :_options + + def initialize(parent, forced_keys) + @parent = parent + @forced_keys = forced_keys + @_options = {} + end + + def method_missing(method_sym, *args, &block) + # Deal with forced keys. + if @forced_keys.include?(method_sym) + @_options[method_sym] = args.first || block if !args.empty? || block + return @_options[method_sym] + end + # Try the resource context. + @parent.send(method_sym, *args, &block) + rescue NameError + # Even though method= in the block will set a variable instead of + # calling method_missing, still try to cope in case of self.method=. + method_sym = method_sym.to_s.chomp('=').to_sym + if !args.empty? || block + @_options[method_sym] = args.first || block + elsif !@_options.include?(method_sym) + # We haven't seen this name before, re-raise the NameError. + raise + end + @_options[method_sym] + end + end + + # @!classmethods + module ClassMethods + # Override the normal #attribute() method to support defining option + # collectors too. + def attribute(name, options={}) + # If present but false-y, make sure it is removed anyway so it + # doesn't confuse ParamsValidate. + if options.delete(:option_collector) + option_collector_attribute(name, options) + else + super + end + end + + # Define an option collector attribute. Normally used via {.attribute}. + # + # @param name [String, Symbol] Name of the attribute to define. + # @param default [Hash] Default value for the options. + # @param parser [Proc, Symbol] Optional parser method. If a symbol it is + # called as a method on self. Takes a non-hash value and returns a + # hash of its parsed representation. + # @param forced_keys [Array, Set] Method names that will be forced + # to be options rather than calls to the parent resource. + def option_collector_attribute(name, default: {}, parser: nil, forced_keys: Set.new) + raise Poise::Error.new("Parser must be a Proc or Symbol: #{parser.inspect}") if parser && !(parser.is_a?(Proc) || parser.is_a?(Symbol)) + # Cast to a set at definition time. + forced_keys = Set.new(forced_keys) unless forced_keys.is_a?(Set) + # Unlike LWRPBase.attribute, I don't care about Ruby 1.8. Worlds tiniest violin. + define_method(name.to_sym) do |arg=nil, &block| + iv_sym = :"@#{name}" + + value = instance_variable_get(iv_sym) || begin + default = instance_eval(&default) if default.is_a?(Chef::DelayedEvaluator) # Handle lazy{} + Mash.new(default) # Wrap in a mash because fuck str vs sym. + end + if arg + if !arg.is_a?(Hash) && parser + arg = case parser + when Proc + instance_exec(arg, &parser) + when Symbol + send(parser, arg) + end + end + raise Exceptions::ValidationFailed, "Option #{name} must be a Hash" if !arg.is_a?(Hash) + # Should this and the update below be a deep merge? + value.update(arg) + end + if block + ctx = OptionEvalContext.new(self, forced_keys) + ctx.instance_exec(&block) + value.update(ctx._options) + end + instance_variable_set(iv_sym, value) + value + end + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/resource_cloning.rb b/cookbooks/poise/files/halite_gem/poise/helpers/resource_cloning.rb new file mode 100644 index 0000000..54c259c --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/resource_cloning.rb @@ -0,0 +1,72 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + module Helpers + # A resource mixin to disable resource cloning. + # + # @since 2.2.0 + # @example + # class MyResource < Chef::Resource + # include Poise::Helpers::ResourceCloning + # end + module ResourceCloning + # Override to disable resource cloning on Chef 12.0. + # + # @api private + def load_prior_resource(*args) + # Do nothing. + end + + # Override to disable resource cloning on Chef 12.1+. + # + # @api private + def load_from(*args) + # Do nothing. + end + + # Monkeypatch for Chef::ResourceBuilder to silence the warning if needed. + # + # @api private + module ResourceBuilderPatch + # @api private + def self.install! + begin + require 'chef/resource_builder' + Chef::ResourceBuilder.send(:prepend, ResourceBuilderPatch) + rescue LoadError + # For 12.0, this is already taken care of. + end + end + + # @api private + def emit_cloned_resource_warning + super unless resource.is_a?(ResourceCloning) + end + + # @api private + def emit_harmless_cloning_debug + super unless resource.is_a?(ResourceCloning) + end + end + + # Install the patch. + ResourceBuilderPatch.install! + + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/resource_name.rb b/cookbooks/poise/files/halite_gem/poise/helpers/resource_name.rb new file mode 100644 index 0000000..c5d40e0 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/resource_name.rb @@ -0,0 +1,107 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/mixin/convert_to_class_name' + + +module Poise + module Helpers + # A resource mixin to automatically set @resource_name. + # + # @since 1.0.0 + # @example + # class MyResource < Chef::Resource + # include Poise::Helpers::ResourceName + # provides(:my_resource) + # end + module ResourceName + def initialize(*args) + super + # If provides() was explicitly set, unconditionally set @resource_name. + # This helps when subclassing core Chef resources which set it + # themselves in #initialize. + if self.class.resource_name(false) + @resource_name = self.class.resource_name + else + @resource_name ||= self.class.resource_name + end + end + + # @!classmethods + module ClassMethods + # Set the DSL name for the the resource class. + # + # @param name [Symbol] Name of the resource. + # @return [void] + # @example + # class MyResource < Chef::Resource + # include Poise::Resource::ResourceName + # provides(:my_resource) + # end + def provides(name, *args, &block) + # Patch self.constantize so this can cope with anonymous classes. + # This does require that the anonymous class define self.name though. + if self.name && respond_to?(:constantize) + old_constantize = instance_method(:constantize) + define_singleton_method(:constantize) do |const_name| + ( const_name == self.name ) ? self : old_constantize.bind(self).call(const_name) + end + end + # Store the name for later. + @provides_name ||= name + # Call the original if present. The defined? is for old Chef. + super(name, *args, &block) if defined?(super) + end + + # Retreive the DSL name for the resource class. If not set explicitly + # via {provides} this will try to auto-detect based on the class name. + # + # @param auto [Boolean] Try to auto-detect based on class name. + # @return [Symbol] + def resource_name(auto=true) + # In 12.4+ we need to proxy through the super class for setting. + return super(auto) if defined?(super) && (auto.is_a?(Symbol) || auto.is_a?(String)) + return @provides_name unless auto + @provides_name || if name + mode = if name.start_with?('Chef::Resource') + [name, 'Chef::Resource'] + else + [name.split('::').last] + end + Chef::Mixin::ConvertToClassName.convert_to_snake_case(*mode).to_sym + elsif defined?(super) + # No name on 12.4+ probably means this is an LWRP, use super(). + super() + end + end + + # Used by Resource#to_text to find the human name for the resource. + # + # @api private + def dsl_name + resource_name.to_s + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/resource_subclass.rb b/cookbooks/poise/files/halite_gem/poise/helpers/resource_subclass.rb new file mode 100644 index 0000000..416d30d --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/resource_subclass.rb @@ -0,0 +1,82 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/error' +require 'poise/helpers/resource_name' + + +module Poise + module Helpers + # A resource mixin to help subclass existing resources. + # + # @since 2.3.0 + module ResourceSubclass + include ResourceName + + module ClassMethods + def subclass_providers!(superclass_resource_name=nil, resource_name: nil) + resource_name ||= self.resource_name + superclass_resource_name ||= if superclass.respond_to?(:resource_name) + superclass.resource_name + elsif superclass.respond_to?(:dsl_name) + superclass.dsl_name + else + raise Poise::Error.new("Unable to determine superclass resource name for #{superclass}. Please specify name manually via subclass_providers!('name').") + end.to_sym + # Deal with the node maps. + node_maps = {} + node_maps['handler map'] = Chef.provider_handler_map if defined?(Chef.provider_handler_map) + node_maps['priority map'] = Chef.provider_priority_map if defined?(Chef.provider_priority_map) + # Patch anything in the descendants tracker. + Chef::Provider.descendants.each do |provider| + node_maps["#{provider} node map"] = provider.node_map if defined?(provider.node_map) + end if defined?(Chef::Provider.descendants) + node_maps.each do |map_name, node_map| + map = node_map.respond_to?(:map, true) ? node_map.send(:map) : node_map.instance_variable_get(:@map) + if map.include?(superclass_resource_name) + Chef::Log.debug("[#{self}] Copying provider mapping in #{map_name} from #{superclass_resource_name} to #{resource_name}") + map[resource_name] = map[superclass_resource_name].dup + end + end + # Add any needed equivalent names. + if superclass.respond_to?(:subclass_resource_equivalents) + subclass_resource_equivalents.concat(superclass.subclass_resource_equivalents) + else + subclass_resource_equivalents << superclass_resource_name + end + subclass_resource_equivalents.uniq! + end + + # An array of names for the resources this class is equivalent to for + # the purposes of provider resolution. + # + # @return [Array] + def subclass_resource_equivalents + @subclass_resource_names ||= [resource_name.to_sym] + end + + # @api private + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end + + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/subcontext_block.rb b/cookbooks/poise/files/halite_gem/poise/helpers/subcontext_block.rb new file mode 100644 index 0000000..facdda9 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/subcontext_block.rb @@ -0,0 +1,72 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/subcontext/resource_collection' + + +module Poise + module Helpers + # A provider mixin to help with creating subcontexts. Mostly for internal + # use within Poise. + # + # @since 1.0.0 + module SubcontextBlock + private + + def subcontext_block(parent_context=nil, &block) + # Setup a subcontext. + parent_context ||= @run_context + sub_run_context = parent_context.dup + # Reset state for the subcontext. In 12.4+ this uses the built-in + # support, otherwise do it manually. + if defined?(sub_run_context.initialize_child_state) + sub_run_context.initialize_child_state + else + # Audits was added in 12.1 I think. + sub_run_context.audits = {} if defined?(sub_run_context.audits) + # Dup and clear to preserve the default behavior without copy-pasta. + sub_run_context.immediate_notification_collection = parent_context.immediate_notification_collection.dup.clear + sub_run_context.delayed_notification_collection = parent_context.delayed_notification_collection.dup.clear + end + # Create the subcollection. + sub_run_context.resource_collection = Poise::Subcontext::ResourceCollection.new(parent_context.resource_collection) + # Create an accessor for the parent run context. + sub_run_context.define_singleton_method(:parent_run_context) { parent_context } + + # Declare sub-resources within the sub-run-context. Since they + # are declared here, they do not pollute the parent run-context. + begin + outer_run_context = @run_context + @run_context = sub_run_context + instance_eval(&block) + ensure + @run_context = outer_run_context + end + + # Return the inner context to do other things with + sub_run_context + end + + def global_resource_collection + collection = @run_context.resource_collection + while collection.respond_to?(:parent) && collection.parent + collection = collection.parent + end + collection + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/subresources.rb b/cookbooks/poise/files/halite_gem/poise/helpers/subresources.rb new file mode 100644 index 0000000..ce0f199 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/subresources.rb @@ -0,0 +1,29 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + module Helpers + # Mixins and helpers for managing subresources, resources with a + # parent/child relationship. + # + # @since 2.0.0 + module Subresources + autoload :Child, 'poise/helpers/subresources/child' + autoload :Container, 'poise/helpers/subresources/container' + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/subresources/child.rb b/cookbooks/poise/files/halite_gem/poise/helpers/subresources/child.rb new file mode 100644 index 0000000..ef6a24a --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/subresources/child.rb @@ -0,0 +1,276 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' + +require 'poise/error' +require 'poise/helpers/subresources/default_containers' + + +module Poise + module Helpers + module Subresources + # A resource mixin for child subresources. + # + # @since 1.0.0 + module Child + # Little class used to fix up the display of subresources in #to_text. + # Without this you get the full parent resource shown for @parent et al. + # @api private + class ParentRef + attr_accessor :resource + + def initialize(resource) + @resource = resource + end + + def inspect + to_text + end + + def to_text + if @resource.nil? + 'nil' + else + @resource.to_s + end + end + end + + # @overload parent() + # Get the parent resource for this child. This may be nil if the + # resource is set to parent_optional = true. + # @return [Chef::Resource, nil] + # @overload parent(val) + # Set the parent resource. The parent can be set as resource + # object, a string (either a bare resource name or a type[name] + # string), or a type:name hash. + # @param val [String, Hash, Chef::Resource] Parent resource to set. + # @return [Chef::Resource, nil] + def parent(*args) + # Lie about this method if the parent type is true. + if self.class.parent_type == true + raise NoMethodError.new("undefined method `parent' for #{self}") + end + _parent(:parent, self.class.parent_type, self.class.parent_optional, self.class.parent_auto, self.class.parent_default, *args) + end + + # Register ourself with parents in case this is not a nested resource. + # + # @api private + def after_created + super + self.class.parent_attributes.each_key do |name| + parent = self.send(name) + parent.register_subresource(self) if parent && parent.respond_to?(:register_subresource) + end + end + + private + + # Generic form of the parent getter/setter. + # + # @since 2.0.0 + # @see #parent + def _parent(name, parent_type, parent_optional, parent_auto, parent_default, *args) + # Allow using a DSL symbol as the parent type. + if parent_type.is_a?(Symbol) + parent_type = Chef::Resource.resource_for_node(parent_type, node) + end + # Grab the ivar for local use. + parent_ref = instance_variable_get(:"@#{name}") + if !args.empty? + val = args.first + if val.nil? + # Unsetting the parent. + parent = parent_ref = nil + else + if val.is_a?(String) && !val.include?('[') + raise Poise::Error.new("Cannot use a string #{name} without defining a parent type") if parent_type == Chef::Resource + # Try to find the most recent instance of parent_type with a + # matching name. This takes subclassing parent_type into account. + found_val = nil + iterator = run_context.resource_collection.respond_to?(:recursive_each) ? :recursive_each : :each + # This will find the last matching value due to overwriting + # found_val as it goes. Will be the nearest match. + run_context.resource_collection.public_send(iterator) do |res| + found_val = res if res.is_a?(parent_type) && res.name == val + end + # If found_val is nil, fall back to using lookup even though + # it won't work with subclassing, better than nothing? + val = found_val || "#{parent_type.resource_name}[#{val}]" + end + if val.is_a?(String) || val.is_a?(Hash) + parent = @run_context.resource_collection.find(val) + else + parent = val + end + if !parent.is_a?(parent_type) + raise Poise::Error.new("Parent resource is not an instance of #{parent_type.name}: #{val.inspect}") + end + parent_ref = ParentRef.new(parent) + end + elsif !parent_ref || !parent_ref.resource + if parent_default + parent = if parent_default.is_a?(Chef::DelayedEvaluator) + instance_eval(&parent_default) + else + parent_default + end + end + # The @parent_ref means we won't run this if we previously set + # ParentRef.new(nil). This means auto-lookup only happens during + # after_created. + if !parent && !parent_ref && parent_auto + # Automatic sibling lookup for sequential composition. + # Find the last instance of the parent class as the default parent. + # This is super flaky and should only be a last resort. + parent = Poise::Helpers::Subresources::DefaultContainers.find(parent_type, run_context, self_resource: self) + end + # Can't find a valid parent, if it wasn't optional raise an error. + raise Poise::Error.new("No #{name} found for #{self}") unless parent || parent_optional + parent_ref = ParentRef.new(parent) + else + parent = parent_ref.resource + end + raise Poise::Error.new("Cannot set the #{name} of #{self} to itself") if parent.equal?(self) + # Store the ivar back. + instance_variable_set(:"@#{name}", parent_ref) + # Return the actual resource. + parent + end + + module ClassMethods + # @overload parent_type() + # Get the class of the default parent link on this resource. + # @return [Class, Symbol] + # @overload parent_type(type) + # Set the class of the default parent link on this resource. + # @param type [Class, Symbol] Class to set. + # @return [Class, Symbol] + def parent_type(type=nil) + if type + raise Poise::Error.new("Parent type must be a class, symbol, or true, got #{type.inspect}") unless type.is_a?(Class) || type.is_a?(Symbol) || type == true + # Setting to true shouldn't actually do anything if a type was already set. + @parent_type = type unless type == true && !@parent_type.nil? + end + # First ancestor_send looks for a non-true && non-default value, + # second one is to check for default vs true if no real value is found. + @parent_type || Poise::Utils.ancestor_send(self, :parent_type, ignore: [Chef::Resource, true]) || Poise::Utils.ancestor_send(self, :parent_type, default: Chef::Resource) + end + + # @overload parent_optional() + # Get the optional mode for the default parent link on this resource. + # @return [Boolean] + # @overload parent_optional(val) + # Set the optional mode for the default parent link on this resource. + # @param val [Boolean] Mode to set. + # @return [Boolean] + def parent_optional(val=nil) + unless val.nil? + @parent_optional = val + end + if @parent_optional.nil? + Poise::Utils.ancestor_send(self, :parent_optional, default: false) + else + @parent_optional + end + end + + # @overload parent_auto() + # Get the auto-detect mode for the default parent link on this resource. + # @return [Boolean] + # @overload parent_auto(val) + # Set the auto-detect mode for the default parent link on this resource. + # @param val [Boolean] Mode to set. + # @return [Boolean] + def parent_auto(val=nil) + unless val.nil? + @parent_auto = val + end + if @parent_auto.nil? + Poise::Utils.ancestor_send(self, :parent_auto, default: true) + else + @parent_auto + end + end + + # @overload parent_default() + # Get the default value for the default parent link on this resource. + # @since 2.3.0 + # @return [Object, Chef::DelayedEvaluator] + # @overload parent_default(val) + # Set the default value for the default parent link on this resource. + # @since 2.3.0 + # @param val [Object, Chef::DelayedEvaluator] Default value to set. + # @return [Object, Chef::DelayedEvaluator] + def parent_default(*args) + unless args.empty? + @parent_default = args.first + end + if defined?(@parent_default) + @parent_default + else + Poise::Utils.ancestor_send(self, :parent_default) + end + end + + # Create a new kind of parent link. + # + # @since 2.0.0 + # @param name [Symbol] Name of the relationship. This becomes a method + # name on the resource instance. + # @param type [Class] Class of the parent. + # @param optional [Boolean] If the parent is optional. + # @param auto [Boolean] If the parent is auto-detected. + # @return [void] + def parent_attribute(name, type: Chef::Resource, optional: false, auto: true, default: nil) + name = :"parent_#{name}" + (@parent_attributes ||= {})[name] = type + define_method(name) do |*args| + _parent(name, type, optional, auto, default, *args) + end + end + + # Return the name of all parent relationships on this class. + # + # @since 2.0.0 + # @return [Hash] + def parent_attributes + {}.tap do |attrs| + # Grab superclass's attributes if possible. + attrs.update(Poise::Utils.ancestor_send(self, :parent_attributes, default: {})) + # Local default parent. + attrs[:parent] = parent_type + # Extra locally defined parents. + attrs.update(@parent_attributes) if @parent_attributes + # Remove anything with the type set to true. + attrs.reject! {|name, type| type == true } + end + end + + # @api private + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/subresources/container.rb b/cookbooks/poise/files/halite_gem/poise/helpers/subresources/container.rb new file mode 100644 index 0000000..7d107f8 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/subresources/container.rb @@ -0,0 +1,229 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/dsl/recipe' + +require 'poise/helpers/subcontext_block' +require 'poise/helpers/subresources/default_containers' + + +module Poise + module Helpers + module Subresources + # A resource mixin for subresource containers. + # + # @since 1.0.0 + module Container + # A resource collection that has much more condensed text output. This + # is used to show the value of @subresources during Chef's error formatting. + # @api private + class NoPrintingResourceCollection < Chef::ResourceCollection + def inspect + to_text + end + + def to_text + "[#{all_resources.map(&:to_s).join(', ')}]" + end + end + + include SubcontextBlock + include Chef::DSL::Recipe + + attr_reader :subresources + attr_reader :subcontexts + + def initialize(*args) + super + @subresources = NoPrintingResourceCollection.new + @subcontexts = [] + end + + def after_created + super + # Register as a default container if needed. + Poise::Helpers::Subresources::DefaultContainers.register!(self, run_context) if self.class.container_default + # Add all internal subresources to the resource collection. + unless @subresources.empty? + Chef::Log.debug("[#{self}] Adding subresources to collection:") + # Because after_create is run before adding the container to the resource collection + # we need to jump through some hoops to get it swapped into place. + self_ = self + order_fixer = Chef::Resource::RubyBlock.new('subresource_order_fixer', @run_context) + # respond_to? is for <= 12.0.2, remove some day when I stop caring. + order_fixer.declared_type = 'ruby_block' if order_fixer.respond_to?(:declared_type=) + order_fixer.block do + Chef::Log.debug("[#{self_}] Running order fixer") + collection = self_.run_context.resource_collection + # Delete the current container resource from its current position. + collection.all_resources.delete(self_) + # Replace the order fixer with the container so it runs before all + # subresources. + collection.all_resources[collection.iterator.position] = self_ + # Hack for Chef 11 to reset the resources_by_name position too. + # @todo Remove this when I drop support for Chef 11. + if resources_by_name = collection.instance_variable_get(:@resources_by_name) + resources_by_name[self_.to_s] = collection.iterator.position + end + # Step back so we re-run the "current" resource, which is now the + # container. + collection.iterator.skip_back + Chef::Log.debug("Collection: #{@run_context.resource_collection.map(&:to_s).join(', ')}") + end + @run_context.resource_collection.insert(order_fixer) + @subcontexts.each do |ctx| + # Copy all resources to the outer context. + ctx.resource_collection.each do |r| + Chef::Log.debug(" * #{r}") + # Fix the subresource to use the outer run context. + r.run_context = @run_context + @run_context.resource_collection.insert(r) + end + # Copy all notifications to the outer context. + %w{immediate delayed}.each do |notification_type| + ctx.send(:"#{notification_type}_notification_collection").each do |key, notifications| + notifications.each do |notification| + parent_notifications = @run_context.send(:"#{notification_type}_notification_collection")[key] + unless parent_notifications.any? { |existing_notification| existing_notification.duplicates?(notification) } + parent_notifications << notification + end + end + end + end + end + Chef::Log.debug("Collection: #{@run_context.resource_collection.map(&:to_s).join(', ')}") + end + end + + def declare_resource(type, name, created_at=nil, &block) + Chef::Log.debug("[#{self}] Creating subresource from #{type}(#{name})") + self_ = self + # Used to break block context, non-local return from subcontext_block. + resource = [] + # Grab the caller so we can make the subresource look like it comes from + # correct place. + created_at ||= caller[0] + # Run this inside a subcontext to avoid adding to the current resource collection. + # It will end up added later, indirected via @subresources to ensure ordering. + @subcontexts << subcontext_block do + namespace = if self.class.container_namespace == true + # If the value is true, use the name of the container resource. + self.name + elsif self.class.container_namespace.is_a?(Proc) + instance_eval(&self.class.container_namespace) + else + self.class.container_namespace + end + sub_name = if name && !name.empty? + if namespace + "#{namespace}::#{name}" + else + name + end + else + # If you pass in nil or '', you just get the namespace or parent name. + namespace || self.name + end + resource << super(type, sub_name, created_at) do + # Apply the correct parent before anything else so it is available + # in after_created for the subresource. It might raise + # NoMethodError is there isn't a real parent. + begin + parent(self_) if respond_to?(:parent) + rescue NoMethodError + # This space left intentionally blank. + end + # Run the resource block. + instance_exec(&block) if block + end + end + # Try and add to subresources. For normal subresources this is handled + # in the after_created. + register_subresource(resource.first) if resource.first + # Return whatever we have + resource.first + end + + # Register a resource as part of this container. Returns true if the + # resource was added to the collection and false if it was already + # known. + # + # @note Return value added in 2.4.0. + # @return [Boolean] + def register_subresource(resource) + subresources.lookup(resource) + false + rescue Chef::Exceptions::ResourceNotFound + Chef::Log.debug("[#{self}] Adding #{resource} to subresources") + subresources.insert(resource) + true + end + + private + + # Thanks Array.flatten, big help you are. Specifically the + # method_missing in the recipe DSL will make a flatten on an array of + # resources fail, so make this safe. + def to_ary + nil + end + + # @!classmethods + module ClassMethods + def container_namespace(val=nil) + @container_namespace = val unless val.nil? + if @container_namespace.nil? + # Not set here, look at the superclass or true by default for backwards compat. + Poise::Utils.ancestor_send(self, :container_namespace, default: true) + else + @container_namespace + end + end + + # @overload container_default() + # Get the default mode for this resource. If false, this resource + # class will not be used for default container lookups. Defaults to + # true. + # @since 2.3.0 + # @return [Boolean] + # @overload container_default(val) + # Set the default mode for this resource. + # @since 2.3.0 + # @param val [Boolean] Default mode to set. + # @return [Boolean] + def container_default(val=nil) + @container_default = val unless val.nil? + if @container_default.nil? + # Not set here, look at the superclass or true by default for backwards compat. + Poise::Utils.ancestor_send(self, :container_default, default: true) + else + @container_default + end + end + + def included(klass) + super + klass.extend(ClassMethods) + klass.const_get(:HIDDEN_IVARS) << :@subcontexts + klass.const_get(:FORBIDDEN_IVARS) << :@subcontexts + end + end + + extend ClassMethods + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/subresources/default_containers.rb b/cookbooks/poise/files/halite_gem/poise/helpers/subresources/default_containers.rb new file mode 100644 index 0000000..2ef26e5 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/subresources/default_containers.rb @@ -0,0 +1,75 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + module Helpers + module Subresources + # Helpers to track default container resources. This is used to find a + # default parent for a child with no parent set. It flat out violates + # encapsulation to allow for the use of default parents to act as + # system-level defaults even when created in a nested scope. + # + # @api private + # @since 2.0.0 + module DefaultContainers + # Mutex to sync access to the containers array. + # + # @see .containers + CONTAINER_MUTEX = Mutex.new + + # Add a resource to the array of default containers. + # + # @param resource [Chef::Resource] Resource to add. + # @param run_context [Chef::RunContext] Context of the current run. + # @return [void] + def self.register!(resource, run_context) + CONTAINER_MUTEX.synchronize do + containers(run_context) << resource + end + end + + # Find a default container for a resource class. + # + # @param klass [Class] Resource class to search for. + # @param run_context [Chef::RunContext] Context of the current run. + # @return [Chef::Resource] + def self.find(klass, run_context, self_resource: nil) + CONTAINER_MUTEX.synchronize do + containers(run_context).reverse_each do |resource| + return resource if resource.is_a?(klass) && (!self_resource || self_resource != resource) + end + # Nothing found. + nil + end + end + + private + + # Get the array of all default container resources. + # + # @note MUST BE CALLED FROM A LOCKED CONTEXT! + # @param run_context [Chef::RunContext] Context of the current run. + # @return [Array] + def self.containers(run_context) + # For test cases where nil gets used sometimes. + return [] unless run_context && run_context.node && run_context.node.run_state + run_context.node.run_state[:poise_default_containers] ||= [] + end + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/template_content.rb b/cookbooks/poise/files/halite_gem/poise/helpers/template_content.rb new file mode 100644 index 0000000..48d8ec9 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/template_content.rb @@ -0,0 +1,168 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider/template_finder' +require 'chef/mixin/template' + +require 'poise/helpers/lazy_default' +require 'poise/helpers/lwrp_polyfill' +require 'poise/helpers/option_collector' +require 'poise/utils' + + +module Poise + module Helpers + # A resource mixin to add a new kind of attribute, template content. TODO + # + # @since 1.0.0 + module TemplateContent + include LazyDefault + include LWRPPolyfill + include OptionCollector + + # @!classmethods + module ClassMethods + def attribute(name, options={}) + if options.delete(:template) + name_prefix = name.empty? ? '' : "#{name}_" + + # If you are reading this, I'm so sorry + # This is used for computing the default cookbook below + parent_filename = caller.first.reverse.split(':', 4).last.reverse + + # If our parent class also declared a template_content attribute on the same name, inherit its options + if superclass.respond_to?("_#{name_prefix}_template_content_options") + options = superclass.send("_#{name_prefix}_template_content_options").merge(options) + end + + # Template source path if using a template + attribute("#{name_prefix}source", kind_of: String) + define_method("_#{name_prefix}source") do + send("#{name_prefix}source") || maybe_eval(options[:default_source]) + end + + # Template cookbook name if using a template + attribute("#{name_prefix}cookbook", kind_of: [String, Symbol], default: lazy do + if send("#{name_prefix}source") + cookbook_name + elsif options[:default_cookbook] + maybe_eval(options[:default_cookbook]) + else + Poise::Utils.find_cookbook_name(run_context, parent_filename) + end + end) + + # Template variables if using a template + attribute("#{name_prefix}options", option_collector: true) + + # Make an alias for #variables to match the template resource. + alias_method("#{name_prefix}variables", "#{name_prefix}options") + + # The big one, get/set content, but if you are getting and no + # explicit content was given, try to render the template + define_method("#{name_prefix}content") do |arg=nil, no_compute=false| + ret = set_or_return("#{name_prefix}content", arg, kind_of: String) + if !ret && !arg && !no_compute + ret = send("_#{name_prefix}content") + # Cache the results for next time + set_or_return("#{name_prefix}content", ret, {}) if ret + end + ret + end + + # Validate that arguments work + define_method("_#{name_prefix}validate") do + if options[:required] && !send("_#{name_prefix}source") && !send("#{name_prefix}content", nil, true) + raise Chef::Exceptions::ValidationFailed, "#{self}: One of #{name_prefix}source or #{name_prefix}content is required" + end + if send("#{name_prefix}source") && send("#{name_prefix}content", nil, true) + raise Chef::Exceptions::ValidationFailed, "#{self}: Only one of #{name_prefix}source or #{name_prefix}content can be specified" + end + end + + # Monkey patch #after_create to run best-effort validation. Arguments + # could be changed after creation, but this gives nicer errors for + # most cases. + unless options[:no_validate_on_create] + old_after_created = instance_method(:after_created) + define_method(:after_created) do + old_after_created.bind(self).call + send("_#{name_prefix}validate") if Array(action) == Array(self.class.default_action) + end + end + + # Compile the needed content + define_method("_#{name_prefix}content") do + # Run validation again + send("_#{name_prefix}validate") + # Get all the relevant parameters + content = send("#{name_prefix}content", nil, true) + source = send("_#{name_prefix}source") + if content + content # I don't think it can ever hit this branch + elsif source + cookbook = send("#{name_prefix}cookbook") + template_options = send("#{name_prefix}options") + send("_#{name_prefix}render_template", source, cookbook, template_options) + else + maybe_eval(options[:default]) + end + end + + # Actually render a template + define_method("_#{name_prefix}render_template") do |source, cookbook, template_options| + all_template_options = {} + all_template_options.update(maybe_eval(options[:default_options])) if options[:default_options] + all_template_options.update(template_options) + all_template_options[:new_resource] = self + finder = Chef::Provider::TemplateFinder.new(run_context, cookbook, node) + context = Chef::Mixin::Template::TemplateContext.new(all_template_options) + context[:node] = node + context[:template_finder] = finder + context.render_template(finder.find(source)) + end + + # Used to check if a parent class already defined a template_content thing here + define_singleton_method("_#{name_prefix}_template_content_options") do + options + end + else + super if defined?(super) + end + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + + private + + # Evaluate lazy blocks if needed + def maybe_eval(val) + if val.is_a?(Chef::DelayedEvaluator) + instance_eval(&val) + else + val + end + end + + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/helpers/win32_user.rb b/cookbooks/poise/files/halite_gem/poise/helpers/win32_user.rb new file mode 100644 index 0000000..4588df4 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/helpers/win32_user.rb @@ -0,0 +1,64 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/utils/win32' + + +module Poise + module Helpers + # A resource mixin to intercept properties named `user`, `group`, or `owner`, + # if their default value is `'root'` and make it work on Windows (and + # FreeBSD, AIX). + # + # @since 2.7.0 + # @example + # class MyResource < Chef::Resource + # include Poise::Helpers::Win32User + # attribute(:user, default: 'root') + # attribute(:group, default: 'root') + # end + # @example Avoiding automatic translation + # class MyResource < Chef::Resource + # include Poise::Helpers::Win32User + # attribute(:user, default: lazy { 'root' }) + # attribute(:group, default: lazy { 'root' }) + # end + module Win32User + # User-ish property names. + # @api private + USER_PROPERTIES = ['user', :user, 'owner', :owner] + + # Group-ish property names. + # @api private + GROUP_PROPERTIES = ['group', :group] + + # Intercept property access to swap out the default value. + # @api private + def set_or_return(symbol, arg, options={}) + if options && options[:default] == 'root' + if USER_PROPERTIES.include?(symbol) && node.platform_family?('windows') + options = options.dup + options[:default] = Poise::Utils::Win32.admin_user + elsif GROUP_PROPERTIES.include?(symbol) + options = options.dup + options[:default] = node['root_group'] + end + end + super(symbol, arg, options) + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/provider.rb b/cookbooks/poise/files/halite_gem/poise/provider.rb new file mode 100644 index 0000000..953e31c --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/provider.rb @@ -0,0 +1,59 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/helpers' +require 'poise/utils' + + +module Poise + # Master provider mixin for Poise-based providers. + # + # @since 1.0.0 + # @example Default helpers. + # class MyProvider < Chef::Provider + # include Poise::Provider + # end + # @example With optional helpers. + # class MyProvider < Chef::Provider + # include Poise::Provider + # poise_inversion(MyResource) + # end + module Provider + include Poise::Helpers::DefinedIn + include Poise::Helpers::LWRPPolyfill + # IncludeRecipe must come after LWRPPolyfill because that pulls in the + # recipe DSL which has its own #include_recipe. + include Poise::Helpers::IncludeRecipe + include Poise::Helpers::NotifyingBlock + include Poise::Utils::ShellOut + + # @!classmethods + module ClassMethods + def poise_inversion(resource, attribute=nil) + include Poise::Helpers::Inversion + inversion_resource(resource) + inversion_attribute(attribute) if attribute + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/resource.rb b/cookbooks/poise/files/halite_gem/poise/resource.rb new file mode 100644 index 0000000..ff8ad05 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/resource.rb @@ -0,0 +1,81 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/helpers' +require 'poise/utils' + + +module Poise + # Master resource mixin for Poise-based resources. + # + # @since 1.0.0 + # @example Default helpers. + # class MyResource < Chef::Resource + # include Poise::Resource + # end + # @example With optional helpers. + # class MyResource < Chef::Resource + # include Poise::Resource + # poise_subresource(MyParent) + # poise_fused + # end + module Resource + include Poise::Helpers::ChefspecMatchers + include Poise::Helpers::DefinedIn + include Poise::Helpers::LazyDefault if Poise::Helpers::LazyDefault.needs_polyfill? + include Poise::Helpers::LWRPPolyfill + include Poise::Helpers::OptionCollector + include Poise::Helpers::ResourceCloning + include Poise::Helpers::ResourceName + include Poise::Helpers::ResourceSubclass + include Poise::Helpers::TemplateContent + include Poise::Helpers::Win32User # Must be after LazyDefault. + include Poise::Utils::ShellOut + + # @!classmethods + module ClassMethods + def poise_subresource_container(namespace=nil, default=nil) + include Poise::Helpers::Subresources::Container + # false is a valid value. + container_namespace(namespace) unless namespace.nil? + container_default(default) unless default.nil? + end + + def poise_subresource(parent_type=nil, parent_optional=nil, parent_auto=nil) + include Poise::Helpers::Subresources::Child + parent_type(parent_type) if parent_type + parent_optional(parent_optional) unless parent_optional.nil? + parent_auto(parent_auto) unless parent_auto.nil? + end + + def poise_fused + include Poise::Helpers::Fused + end + + def poise_inversion(options_resource=nil) + include Poise::Helpers::Inversion + inversion_options_resource(true) unless options_resource == false + end + + def included(klass) + super + klass.extend(ClassMethods) + end + end + + extend ClassMethods + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/subcontext.rb b/cookbooks/poise/files/halite_gem/poise/subcontext.rb new file mode 100644 index 0000000..278d973 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/subcontext.rb @@ -0,0 +1,27 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + # Helpers and whatnot for dealing with subcontexts. + # + # @api private + # @since 2.0.0 + module Subcontext + autoload :ResourceCollection, 'poise/subcontext/resource_collection' + autoload :Runner, 'poise/subcontext/runner' + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/subcontext/resource_collection.rb b/cookbooks/poise/files/halite_gem/poise/subcontext/resource_collection.rb new file mode 100644 index 0000000..cacd32c --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/subcontext/resource_collection.rb @@ -0,0 +1,75 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource_collection' + + +module Poise + module Subcontext + # A subclass of the normal Chef ResourceCollection that creates a partially + # isolated set of resources. Notifications and other resources lookups can + # propagate out to parent contexts but not back in. This is used to allow + # black-box resources that are still aware of things in upper contexts. + # + # @api private + # @since 1.0.0 + class ResourceCollection < Chef::ResourceCollection + attr_accessor :parent + + def initialize(parent) + @parent = parent + super() + end + + def lookup(resource) + super + rescue Chef::Exceptions::ResourceNotFound + @parent.lookup(resource) + end + + # Iterate over all resources, expanding parent context in order. + # + # @param block [Proc] Iteration block + # @return [void] + def recursive_each(&block) + if @parent + if @parent.respond_to?(:recursive_each) + @parent.recursive_each(&block) + else + @parent.each(&block) + end + end + each(&block) + end + + # Iterate over all resources in reverse order. + # + # @since 2.3.0 + # @param block [Proc] Iteration block + # @return [void] + def reverse_recursive_each(&block) + reverse_each(&block) + if @parent + if @parent.respond_to?(:recursive_each) + @parent.reverse_recursive_each(&block) + else + @parent.reverse_each(&block) + end + end + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/subcontext/runner.rb b/cookbooks/poise/files/halite_gem/poise/subcontext/runner.rb new file mode 100644 index 0000000..aaa5e44 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/subcontext/runner.rb @@ -0,0 +1,55 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/runner' + + +module Poise + module Subcontext + # A subclass of the normal Chef Runner that migrates delayed notifications + # to the enclosing run_context instead of running them at the end of the + # subcontext convergence. + # + # @api private + # @since 1.0.0 + class Runner < Chef::Runner + def initialize(resource, *args) + super(*args) + @resource = resource + end + + def run_delayed_notifications(error=nil) + # If there is an error, just do the normal thing. The return shouldn't + # ever fire because the superclass re-raises if there is an error. + return super if error + delayed_actions.each do |notification| + if @resource.run_context.respond_to?(:add_delayed_action) + @resource.run_context.add_delayed_action(notification) + else + notifications = run_context.parent_run_context.delayed_notifications(@resource) + if notifications.any? { |existing_notification| existing_notification.duplicates?(notification) } + Chef::Log.info( "#{@resource} not queuing delayed action #{notification.action} on #{notification.resource}"\ + " (delayed), as it's already been queued") + else + notifications << notification + end + end + end + end + + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/utils.rb b/cookbooks/poise/files/halite_gem/poise/utils.rb new file mode 100644 index 0000000..9e05270 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/utils.rb @@ -0,0 +1,181 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'poise/error' + + +module Poise + module Utils + autoload :ResourceProviderMixin, 'poise/utils/resource_provider_mixin' + autoload :ShellOut, 'poise/utils/shell_out' + autoload :Win32, 'poise/utils/win32' + + extend self + + # Find the cookbook name for a given filename. The can used to find the + # cookbook that corresponds to a caller of a file. + # + # @param run_context [Chef::RunContext] Context to check. + # @param filename [String] Absolute filename to check for. + # @return [String] + # @example + # def my_thing + # caller_filename = caller.first.split(':').first + # cookbook = Poise::Utils.find_cookbook_name(run_context, caller_filename) + # # ... + # end + def find_cookbook_name(run_context, filename) + possibles = {} + Poise.debug("[Poise] Checking cookbook for #{filename.inspect}") + run_context.cookbook_collection.each do |name, ver| + # This special method is added by Halite::Gem#as_cookbook_version. + if ver.respond_to?(:halite_root) + # The join is there because ../poise-ruby/lib starts with ../poise so + # we want a trailing /. + if filename.start_with?(File.join(ver.halite_root, '')) + Poise.debug("[Poise] Found matching halite_root in #{name}: #{ver.halite_root.inspect}") + possibles[ver.halite_root] = name + end + else + Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |seg| + ver.segment_filenames(seg).each do |file| + if ::File::ALT_SEPARATOR + file = file.gsub(::File::ALT_SEPARATOR, ::File::SEPARATOR) + end + # Put this behind an environment variable because it is verbose + # even for normal debugging-level output. + Poise.debug("[Poise] Checking #{seg} in #{name}: #{file.inspect}") + if file == filename + Poise.debug("[Poise] Found matching #{seg} in #{name}: #{file.inspect}") + possibles[file] = name + end + end + end + end + end + raise Poise::Error.new("Unable to find cookbook for file #{filename.inspect}") if possibles.empty? + # Sort the items by matching path length, pick the name attached to the longest. + possibles.sort_by{|key, value| key.length }.last[1] + end + + # Try to find an ancestor to call a method on. + # + # @since 2.2.3 + # @since 2.3.0 + # Added ignore parameter. + # @param obj [Object] Self from the caller. + # @param msg [Symbol] Method to try to call. + # @param args [Array] Method arguments. + # @param default [Object] Default return value if no valid ancestor exists. + # @param ignore [Array] Return value to ignore when scanning ancesors. + # @return [Object] + # @example + # val = @val || Poise::Utils.ancestor_send(self, :val) + def ancestor_send(obj, msg, *args, default: nil, ignore: [default]) + # Class is a subclass of Module, if we get something else use its class. + obj = obj.class unless obj.is_a?(Module) + ancestors = [] + if obj.respond_to?(:superclass) + # Check the superclass first if present. + ancestors << obj.superclass + end + # Make sure we don't check obj itself. + ancestors.concat(obj.ancestors.drop(1)) + ancestors.each do |mod| + if mod.respond_to?(msg) + val = mod.send(msg, *args) + # If we get the default back, assume we should keep trying. + return val unless ignore.include?(val) + end + end + # Nothing valid found, use the default. + default + end + + # Create a helper to invoke a module with some parameters. + # + # @since 2.3.0 + # @param mod [Module] The module to wrap. + # @param block [Proc] The module to implement to parameterization. + # @return [void] + # @example + # module MyMixin + # def self.my_mixin_name(name) + # # ... + # end + # end + # + # Poise::Utils.parameterized_module(MyMixin) do |name| + # my_mixin_name(name) + # end + def parameterized_module(mod, &block) + raise Poise::Error.new("Cannot parameterize an anonymous module") unless mod.name && !mod.name.empty? + parent_name_parts = mod.name.split(/::/) + # Grab the last piece which will be the method name. + mod_name = parent_name_parts.pop + # Find the enclosing module or class object. + parent = parent_name_parts.inject(Object) {|memo, name| memo.const_get(name) } + # Object is a special case since we need #define_method instead. + method_type = if parent == Object + :define_method + else + :define_singleton_method + end + # Scoping hack. + self_ = self + # Construct the method. + parent.send(method_type, mod_name) do |*args| + self_.send(:check_block_arity!, block, args) + # Create a new anonymous module to be returned from the method. + Module.new do + # Fake the name. + define_singleton_method(:name) do + super() || mod.name + end + + # When the stub module gets included, activate our behaviors. + define_singleton_method(:included) do |klass| + super(klass) + klass.send(:include, mod) + klass.instance_exec(*args, &block) + end + end + end + end + + private + + # Check that the given arguments match the given block. This is needed + # because Ruby will nil-pad mismatched argspecs on blocks rather than error. + # + # @since 2.3.0 + # @param block [Proc] Block to check. + # @param args [Array] Arguments to check. + # @return [void] + def check_block_arity!(block, args) + # Convert the block to a lambda-style proc. You can't make this shit up. + obj = Object.new + obj.define_singleton_method(:block, &block) + block = obj.method(:block).to_proc + # Check + required_args = block.arity < 0 ? ~block.arity : block.arity + if args.length < required_args || (block.arity >= 0 && args.length > block.arity) + raise ArgumentError.new("wrong number of arguments (#{args.length} for #{required_args}#{block.arity < 0 ? '+' : ''})") + end + end + + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/utils/resource_provider_mixin.rb b/cookbooks/poise/files/halite_gem/poise/utils/resource_provider_mixin.rb new file mode 100644 index 0000000..fba69d4 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/utils/resource_provider_mixin.rb @@ -0,0 +1,65 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + module Utils + # A mixin to dispatch other mixins with resource and provider + # implementations. The module this is included in must have Resource and + # Provider sub-modules. + # + # @since 2.0.0 + # @example + # module MyHelper + # include Poise::Utils::ResourceProviderMixin + # module Resource + # # ... + # end + # + # module Provider + # # ... + # end + # end + module ResourceProviderMixin + def self.included(klass) + # Warning here be dragons. + # Create a new anonymous module, klass will be the module that + # actually included ResourceProviderMixin. We want to keep a reference + # to that locked down so that we can close over it and use it in the + # "real" .included defined below to find the original relative consts. + mod = Module.new do + # Use define_method instead of def so we can close over klass and mod. + define_method(:included) do |inner_klass| + # Has to be explicit because super inside define_method. + super(inner_klass) + # Cargo this .included to things which include us. + inner_klass.extend(mod) + # Dispatch to submodules, inner_klass is the most recent includer. + if inner_klass < Chef::Resource || inner_klass.name.to_s.end_with?('::Resource') + # Use klass::Resource to look up relative to the original module. + inner_klass.class_exec { include klass::Resource } + elsif inner_klass < Chef::Provider || inner_klass.name.to_s.end_with?('::Provider') + # As above, klass::Provider. + inner_klass.class_exec { include klass::Provider } + end + end + end + # Add our .included to the original includer. + klass.extend(mod) + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/utils/shell_out.rb b/cookbooks/poise/files/halite_gem/poise/utils/shell_out.rb new file mode 100644 index 0000000..0231e14 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/utils/shell_out.rb @@ -0,0 +1,90 @@ +# +# Copyright 2015-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'etc' + +require 'chef/mixin/shell_out' + + +module Poise + module Utils + # A mixin to provider a better shell_out. + # + # @since 2.5.0 + # @example + # Poise::Utils::ShellOut.poise_shell_out('ruby myapp.rb', user: 'myuser') + module ShellOut + extend self + include Chef::Mixin::ShellOut + + # An enhanced version of Chef's `shell_out` which sets some default + # parameters. If possible it will set $HOME, $USER, $LOGNAME, and the + # group to run as. + # + # @param command_args [Array] Command arguments to be passed to `shell_out`. + # @param options [Hash] Options to be passed to `shell_out`, + # with modifications. + # @return [Mixlib::ShellOut] + def poise_shell_out(*command_args, **options) + # Allow the env option shorthand. + options[:environment] ||= {} + if options[:env] + options[:environment].update(options[:env]) + options.delete(:env) + end + # Convert environment keys to strings to be safe. + options[:environment] = options[:environment].inject({}) do |memo, (key, value)| + memo[key.to_s] = value.to_s + memo + end + # Populate some standard environment variables. + ent = begin + if options[:user].is_a?(Integer) + Etc.getpwuid(options[:user]) + elsif options[:user] + Etc.getpwnam(options[:user]) + end + rescue ArgumentError + nil + end + username = ent ? ent.name : options[:name] + if username + options[:environment]['HOME'] ||= Dir.home(username) + options[:environment]['USER'] ||= username + # On the off chance they set one manually but not the other. + options[:environment]['LOGNAME'] ||= options[:environment]['USER'] + end + # Set the default group on Unix. + options[:group] ||= ent.gid if ent + # Mixlib-ShellOut doesn't support array commands on Windows and has + # super wonky escaping for cmd.exe. + if respond_to?(:node) && node.platform_family?('windows') + command_args = [Poise::Utils::Win32.reparse_command(*command_args)] + end + # Call Chef's shell_out wrapper. + shell_out(*command_args, **options) + end + + # The `error!` version of {#poise_shell_out}. + # + # @see #poise_shell_out + # @return [Mixlib::ShellOut] + def poise_shell_out!(*command_args) + poise_shell_out(*command_args).tap(&:error!) + end + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/utils/win32.rb b/cookbooks/poise/files/halite_gem/poise/utils/win32.rb new file mode 100644 index 0000000..0cc6f9d --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/utils/win32.rb @@ -0,0 +1,127 @@ +# +# Copyright 2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'shellwords' + + +module Poise + module Utils + # Utilities for working with Windows. + # + # @since 2.7.0 + module Win32 + extend self + + # Code borrowed from https://github.com/chef-cookbooks/chef-client/blob/master/libraries/helpers.rb + # Used under the terms of the Apache v2 license. + # Copyright 2012-2016, John Dewey + + # Run a WMI query and extracts a property. This assumes Chef has already + # loaded the win32 libraries. + # + # @api private + # @param wmi_property [Symbol] Property to extract. + # @param wmi_query [String] Query to run. + # @return [String] + def wmi_property_from_query(wmi_property, wmi_query) + @wmi = ::WIN32OLE.connect('winmgmts://') + result = @wmi.ExecQuery(wmi_query) + return nil unless result.each.count > 0 + result.each.next.send(wmi_property) + end + + # Find the name of the Administrator user, give or take localization. + # + # @return [String] + def admin_user + if defined?(::WIN32OLE) + wmi_property_from_query(:name, "select * from Win32_UserAccount where sid like 'S-1-5-21-%-500' and LocalAccount=True") + else + # Warn except under ChefSpec because it will just annoy people. + Chef::Log.warn('[Poise::Utils::Win32] Unable to query admin user, WIN32OLE not available') unless defined?(ChefSpec) + 'Administrator' + end + end + + # Escaping that is compatible with CommandLineToArgvW. Based on + # https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ + # + # @api private + # @param string [String] String to escape. + # @return [String] + def argv_quote(string, force_quote: false) + if !force_quote && !string.empty? && string !~ /[ \t\n\v"]/ + # Nothing fancy, no escaping needed. + string + else + command_line = '"' + i = 0 + while true + number_backslashes = 0 + + while i != string.size && string[i] == '\\' + i += 1 + number_backslashes += 1 + end + + if i == string.size + # Escape all backslashes, but let the terminating + # double quotation mark we add below be interpreted + # as a metacharacter. + command_line << '\\' * (number_backslashes * 2) + break + elsif string[i] == '"' + # Escape all backslashes and the following + # double quotation mark. + command_line << '\\' * ((number_backslashes * 2) + 1) + command_line << '"' + else + # Backslashes aren't special here. + command_line << '\\' * number_backslashes + command_line << string[i] + end + i += 1 + end + command_line << '"' + command_line + end + end + + # Take a string or array command in the format used by shell_out et al and + # create something we can use on Windows. + # + # @ + def reparse_command(*args) + array_mode = !(args.length == 1 && args.first.is_a?(String)) + # At some point when mixlib-shellout groks array commands on Windows, + # we should support that here. + parsed_args = array_mode ? args.flatten : Shellwords.split(args.first) + cmd = parsed_args.map {|s| argv_quote(s) }.join(' ') + if array_mode + # This fails on non-Windows because of win32/process. + require 'mixlib/shellout/windows' + if Mixlib::ShellOut::Windows::Utils.should_run_under_cmd?(cmd) + # If we are in array mode, try to make cmd.exe keep its grubby paws + # off our metacharacters. + cmd = cmd.each_char.map {|c| '^'+c }.join('') + end + end + cmd + end + + end + end +end diff --git a/cookbooks/poise/files/halite_gem/poise/version.rb b/cookbooks/poise/files/halite_gem/poise/version.rb new file mode 100644 index 0000000..18c9006 --- /dev/null +++ b/cookbooks/poise/files/halite_gem/poise/version.rb @@ -0,0 +1,20 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +module Poise + VERSION = '2.7.2' +end diff --git a/cookbooks/poise/libraries/default.rb b/cookbooks/poise/libraries/default.rb new file mode 100644 index 0000000..9731909 --- /dev/null +++ b/cookbooks/poise/libraries/default.rb @@ -0,0 +1,18 @@ +# +# Copyright 2013-2016, Noah Kantrowitz +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +raise 'Halite is not compatible with no_lazy_load false, please set no_lazy_load true in your Chef configuration file.' unless Chef::Config[:no_lazy_load] +$LOAD_PATH << File.expand_path('../../files/halite_gem', __FILE__) diff --git a/cookbooks/poise/metadata.json b/cookbooks/poise/metadata.json new file mode 100644 index 0000000..5fb9bb6 --- /dev/null +++ b/cookbooks/poise/metadata.json @@ -0,0 +1 @@ +{"name":"poise","version":"2.7.2","description":"Helpers for writing extensible Chef cookbooks.","long_description":"# Poise\n\n[![Build Status](https://img.shields.io/travis/poise/poise.svg)](https://travis-ci.org/poise/poise)\n[![Gem Version](https://img.shields.io/gem/v/poise.svg)](https://rubygems.org/gems/poise)\n[![Cookbook Version](https://img.shields.io/cookbook/v/poise.svg)](https://supermarket.chef.io/cookbooks/poise)\n[![Coverage](https://img.shields.io/codecov/c/github/poise/poise.svg)](https://codecov.io/github/poise/poise)\n[![Gemnasium](https://img.shields.io/gemnasium/poise/poise.svg)](https://gemnasium.com/poise/poise)\n[![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\n## What is Poise?\n\nThe poise cookbook is a set of libraries for writing reusable cookbooks. It\nprovides helpers for common patterns and a standard structure to make it easier to create flexible cookbooks.\n\n## Writing your first resource\n\nRather than LWRPs, Poise promotes the idea of using normal, or \"heavy weight\"\nresources, while including helpers to reduce much of boilerplate needed for this. Each resource goes in its own file under `libraries/` named to match\nthe resource, which is in turn based on the class name. This means that the file `libraries/my_app.rb` would contain `Chef::Resource::MyApp` which maps to the resource `my_app`.\n\nAn example of a simple shell to start from:\n\n```ruby\nrequire 'poise'\nrequire 'chef/resource'\nrequire 'chef/provider'\n\nmodule MyApp\n class Resource < Chef::Resource\n include Poise\n provides(:my_app)\n actions(:enable)\n\n attribute(:path, kind_of: String)\n # Other attribute definitions.\n end\n\n class Provider < Chef::Provider\n include Poise\n provides(:my_app)\n\n def action_enable\n notifying_block do\n ... # Normal Chef recipe code goes here\n end\n end\n end\nend\n```\n\nStarting from the top, first we require the libraries we will be using. Then we\ncreate a module to hold our resource and provider. If your cookbook declares\nmultiple resources and/or providers, you might want additional nesting here.\nThen we declare the resource class, which inherits from `Chef::Resource`. This\nis similar to the `resources/` file in an LWRP, and a similar DSL can be used.\nWe then include the `Poise` mixin to load our helpers, and then call\n`provides(:my_app)` to tell Chef this class will implement the `my_app`\nresource. Then we use the familiar DSL, though with a few additions we'll cover\nlater.\n\nThen we declare the provider class, again similar to the `providers/` file in an\nLWRP. We include the `Poise` mixin again to get access to all the helpers and\ncall `provides()` to tell Chef what provider this is. Rather than use the\n`action :enable do ... end` DSL from LWRPs, we just define the action method\ndirectly. The implementation of action comes from a block of recipe code\nwrapped with `notifying_block` to capture changes in much the same way as\n`use_inline_resources`, see below for more information about all the features of\n`notifying_block`.\n\nWe can then use this resource like any other Chef resource:\n\n```ruby\nmy_app 'one' do\n path '/tmp'\nend\n```\n\n## Helpers\n\nWhile not exposed as a specific method, Poise will automatically set the\n`resource_name` based on the class name.\n\n### Notifying Block\n\nAs mentioned above, `notifying_block` is similar to `use_inline_resources` in LWRPs. Any Chef resource created inside the block will be converged in a sub-context and if any have updated it will trigger notifications on the current resource. Unlike `use_inline_resources`, resources inside the sub-context can still see resources outside of it, with lookups propagating up sub-contexts until a match is found. Also any delayed notifications are scheduled to run at the end of the main converge cycle, instead of the end of this inner converge.\n\nThis can be used to write action methods using the normal Chef recipe DSL, while still offering more flexibility through subclassing and other forms of code reuse.\n\n### Include Recipe\n\nIn keeping with `notifying_block` to implement action methods using the Chef DSL, Poise adds an `include_recipe` helper to match the method of the same name in recipes. This will load and converge the requested recipe.\n\n### Resource DSL\n\nTo make writing resource classes easier, Poise exposes a DSL similar to LWRPs for defining actions and attributes. Both `actions` and\n`default_action` are just like in LWRPs, though `default_action` is rarely needed as the first action becomes the default. `attribute` is also available just like in LWRPs, but with some enhancements noted below.\n\nOne notable difference over the standard DSL method is that Poise attributes\ncan take a block argument.\n\n#### Template Content\n\nA common pattern with resources is to allow passing either a template filename or raw file content to be used in a configuration file. Poise exposes a new attribute flag to help with this behavior:\n\n```ruby\nattribute(:name, template: true)\n```\n\nThis creates four methods on the class, `name_source`, `name_cookbook`,\n`name_content`, and `name_options`. If the name is set to `''`, no prefix is applied to the function names. The content method can be set directly, but if not set and source is set, then it will render the template and return it as a string. Default values can also be set for any of these:\n\n```ruby\nattribute(:name, template: true, default_source: 'app.cfg.erb',\n default_options: {host: 'localhost'})\n```\n\nAs an example, you can replace this:\n\n```ruby\nif new_resource.source\n template new_resource.path do\n source new_resource.source\n owner 'app'\n group 'app'\n variables new_resource.options\n end\nelse\n file new_resource.path do\n content new_resource.content\n owner 'app'\n group 'app'\n end\nend\n```\n\nwith simply:\n\n```ruby\nfile new_resource.path do\n content new_resource.content\n owner 'app'\n group 'app'\nend\n```\n\nAs the content method returns the rendered template as a string, this can also\nbe useful within other templates to build from partials.\n\n#### Lazy Initializers\n\nOne issue with Poise-style resources is that when the class definition is executed, Chef hasn't loaded very far so things like the node object are not\nyet available. This means setting defaults based on node attributes does not work directly:\n\n```ruby\nattribute(:path, default: node['myapp']['path'])\n...\nNameError: undefined local variable or method 'node'\n```\n\nTo work around this, Poise extends the idea of lazy initializers from Chef recipes to work with resource definitions as well:\n\n```ruby\nattribute(:path, default: lazy { node['myapp']['path'] })\n```\n\nThese initializers are run in the context of the resource object, allowing\ncomplex default logic to be moved to a method if desired:\n\n```ruby\nattribute(:path, default: lazy { my_default_path })\n\ndef my_default_path\n ...\nend\n```\n\n#### Option Collector\n\nAnother common pattern with resources is to need a set of key/value pairs for\nconfiguration data or options. This can done with a simple Hash, but an option collector attribute can offer a nicer syntax:\n\n```ruby\nattribute(:mydata, option_collector: true)\n...\n\nmy_app 'name' do\n mydata do\n key1 'value1'\n key2 'value2'\n end\nend\n```\n\nThis will be converted to `{key1: 'value1', key2: 'value2'}`. You can also pass a Hash to an option collector attribute just as you would with a normal attribute.\n\n## Debugging Poise\n\nPoise has its own extra-verbose level of debug logging that can be enabled in\nthree different ways. You can either set the environment variable `$POISE_DEBUG`,\nset a node attribute `node['POISE_DEBUG']`, or touch the file `/POISE_DEBUG`.\nYou will see a log message `Extra verbose logging enabled` at the start of the\nrun to confirm Poise debugging has been enabled. Make sure you also set Chef's\nlog level to `debug`, usually via `-l debug` on the command line.\n\n## Upgrading from Poise 1.x\n\nThe biggest change when upgrading from Poise 1.0 is that the mixin is no longer\nloaded automatically. You must add `require 'poise'` to your code is you want to\nload it, as you would with normal Ruby code outside of Chef. It is also highly\nrecommended to add `provides(:name)` calls to your resources and providers, this\nwill be required in Chef 13 and will display a deprecation warning if you do\nnot. This also means you can move your code out of the `Chef` module namespace\nand instead declare it in your own namespace. An example of this is shown above.\n\n## Sponsors\n\nThe Poise test server infrastructure is generously sponsored by [Rackspace](https://rackspace.com/). Thanks Rackspace!\n\n## License\n\nCopyright 2013-2016, Noah Kantrowitz\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainer":"Noah Kantrowitz","maintainer_email":"noah@coderanger.net","license":"Apache 2.0","platforms":{},"dependencies":{},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{},"source_url":"https://github.com/poise/poise","issues_url":"https://github.com/poise/poise/issues","chef_version":"~> 12","ohai_version":{}} \ No newline at end of file diff --git a/cookbooks/postgresql/.foodcritic b/cookbooks/postgresql/.foodcritic new file mode 100644 index 0000000..41c5512 --- /dev/null +++ b/cookbooks/postgresql/.foodcritic @@ -0,0 +1,2 @@ +~FC037 +~FC016 diff --git a/cookbooks/postgresql/CHANGELOG.md b/cookbooks/postgresql/CHANGELOG.md index f794009..5c4f4fa 100644 --- a/cookbooks/postgresql/CHANGELOG.md +++ b/cookbooks/postgresql/CHANGELOG.md @@ -1,44 +1,197 @@ -postgresql Cookbook CHANGELOG -============================= +# postgresql Cookbook CHANGELOG + This file is used to list changes made in each version of the postgresql cookbook. -v4.0.0 ------ -* Potential breaking change: Restructured default attributes to avoid compile time deriving other attribute values from value of the `node[‘postgresql’][‘version’]` -(#313, #302, #295, #288, #280, #261, #260, #254, #248, #217, #214, #167, #143) -* Correct issues which caused the inability to override installation version defaults -* Correct issues which caused configuration file entries with miss matching version numbers and incorrect file system paths being defined -* Remove method pgdgrepo_rpm_info compile time use of derived attributes case many issues -* Use correct directory path and check for the correct not_if condition to determine if the database has been initialized -* Ensure that correct packages are installed in all scenarios where pg gem is compiled -* Fix errors in configuration files for unix_socket_directory and unix_socket_directories -* Updates to test-kitchen suite configuration -* Added more grey hair to my beard +## v6.1.1 (2017-03-08) -v3.4.24 -------- -* Corrections to address repositories signed with newer certificates that some distributions have in their default ca-certificates package -* Updates to more accurately determine distributions service init systems adds better support for systemd systems -* Correct how version attribute is evaluated in certain places -* test-kitchen suite configuration corrections -* Opensuse support +- Fix pg gem installation on non-omnibus chef runs +- Resolve resource cloning deprecation warnings in the ruby recipe +- Fix issues resolving the timezone on CentOS 7 and probably other distros +- Test with Delivery local instead of Rake + +## v6.1.0 (2017-02-18) + +- Fix a method name conflict that caused errors if Chef Sugar was also being used on the run list +- Revert a previous PR that added support for Postgresql 9.6 as it introduced incorrect configuration values +- Added Fedora 25 support for pgdg packages +- Added RHEL 5 support for Postgresql 9.4 pgdg packages +- Removed testing for RHEL 5 and Ubuntu 12.04 as they are scheduled for EoL in the near future +- Improvements to Test Kitchen testing to allow more extensive testing in Travis CI +- Fixed the client recipe on Fedora +- Added Inspec tests for client installs + +## v6.0.1 (2017-01-04 + +- Fix systemd unit file template + +## v6.0.0 (2017-01-03) + +- This cookbook now requires Chef 12.1 or later +- Removed the dependency on the apt cookbook as this functionality is built into modern chef client releases +- Added a new custom resource for installing extensions. This acts as a replacement for the contrib recipe with minimal backwards compatibility. You can now install / remove extensions into any database. This adds the compat_resource cookbook dependency so we can continue to support Chef 12.1-12.4, which lack custom resource support. +- The unused get_result_orig helper has been removed. If you utilized this you'll want to move it to your own wrapper cookbook +- Updates for compatibility with Postgresql 9.5 and 9.6 +- Fixed client package installation on openSUSE Leap 42.2 +- ca-certificates recipe has been deprecated. If ca-certificates package needs to be upgraded the user should do so prior to including this recipe. Package upgrades in community cookbooks are generally a bad idea as this bring in updated packages to production systems. The recipe currently warns if used and will be removed with the next major cookbook release. +- Fixed RHEL platform detection in the Ruby recipe +- systemd fixes for RHEL systems +- Fix systemd service file include when using pgdg packages +- Package installation now uses multi-package installs to speed up converge times +- Added integration testing in Travis of the client recipe using a new test cookbook. This will be expanded in the future to cover server installation as well +- Expanded the specs to test converges on multiple platforms + +## v5.2.0 (2016-12-30) + +- Updated contacts and links to point to Sous Chefs now +- Added a Code of Conduct (the Chef CoC) +- Removed duplicate platforms in the metadata +- Fix Chef runs with local mode in the server recipe +- Fix the ruby recipe to not fail when you specify enabling both the apt and yum repos for mixed distro environments +- Set the postgresql data directory to 700 permissions +- Added node['postgresql']['pg_gem']['version'] to specify the version of the pg gem to install +- Cookstyle fixes for the latest cookstyle release +- Removed test deps from the Gemfile. Rely on ChefDK for base testing deps instead + +## v5.1.0 (2016-11-01) + +- Maintenance of this cookbook has been migrated from Heavy Water to Sous Chefs - +- Add support for Chef-Zero (local mode) +- Don't hardcode the UID / GID on RHEL/Amazon/Suse platforms +- Add PGDG yum RPMs for 9.5 / 9.6 + +## v5.0.0 (2016-10-25) + +### Breaking changes + +- Switched from Librarian to Berkshelf +- Remove support for the following platforms + + - SLES < 12 + - openSUSE < 13 + - Debian < 7 + - Ubuntu < 12.04 + - RHEL < 6 + - Amazon < 2013 + - Unsupported (EOL) Fedora releases + +### Other changes + +- Added support for Ubuntu 16.04 +- Loosened cookbook dependencies to not prevent pulling in the latest community cookbooks +- Added chef_version metadata +- Switched from rubocop to cookstyle and fix all warnings +- Removed minitests and the minitest handler +- Added support for opensuse / opensuseleap +- Added support for Fedora 23/24 +- Added a chefignore file to limit the files uploaded to the chef server +- Updated Test Kitchen config to test on modern platform releases +- Added a Rakefile and updated Travis to test with ChefDK and that rakefile +- Avoid installing packages included in build-essential twice in the ruby recipe +- Require at least build-essential 2.0 +- Don't cleanup the old PPA files in the apt_pgdg_postgresql recipe anymore. These should be long gone everywhere +- Remove logic in the apt_pgdg_postgresql recipe that made Chef fail when new distro releases came out +- Avoid node.set deprecation warnings +- Avoid managed_home deprecation warnings in server_redhat recipe + +## v4.0.6 + +- Add 16.04 Xenial to the allowed list + +## v4.0.4 + +- Add leading pound symbol on pg_hba.conf template comment line +- Update gem install for compile_time to correct deprication warning +- Add support Ubuntu Wily Werewolf pgdg apt repository +- test-kitchen platforms for Centos 7.2 and Ubuntu 15.04 +- Fixes PostgreSQL version & package name defaults for EL7 distros +- Add appropriate systemd unit file overrides for EL7 distros + +## v4.0.2 + +- Add Code of Conduct +- Add Rubocop +- Clean up of syntax in many places as result of adding and evaluating Rubocop +- Updates to test-kitchen.yml +- added additional attribute for people who are importing pgdg packages for internal repositories + + - `default['postgresql']['use_pgdg_packages'] = false` + +## v4.0.0 + +**WARNING: Please read carefully through the stated changes, as they probably will break your current setup and can result in duplicate postgresql versions being installed, configuration corruption and data loss! This list might not be complete, so be careful when using the 4.x version and make sure to test it extensively before production use!** + +When in doubt, put the following in your `Berksfile` until you are ready to upgrade: + +```ruby +cookbook 'postgresql', '~> 3.4.0' +``` + +- Potential breaking change: Restructured default attributes to avoid compile time deriving other attribute values from value of the `node[‘postgresql’][‘version’]` (#313, #302, #295, #288, #280, #261, #260, #254, #248, #217, #214, #167, #143). If you specify a custom postgresql version, make sure to adapt the following attributes as well: + +```ruby +default['postgresql']['dir'] = "/etc/postgresql/#{node['postgresql']['version']}/main" +default['postgresql']['client']['packages'] = [ "postgresql-client-#{node['postgresql']['version']}", 'libpq-dev' ] +default['postgresql']['server']['packages'] = [ "postgresql-#{node['postgresql']['version']}" ] +default['postgresql']['contrib']['packages'] = [ "postgresql-contrib-#{node['postgresql']['version']}" ] +``` + +- Potential breaking change: SSL configuration parameters. Due to the new structuring, make sure you set all SSL attributes to `override` when specifying them in a cookbook: + +```ruby +override['postgresql']['config']['ssl'] = true +override['postgresql']['config']['ssl_cert_file'] = "/path/to/cert.crt" +override['postgresql']['config']['ssl_key_file'] = "/path/to/cert.key" +override['postgresql']['config']['ssl_ciphers'] = "" +``` + +- Potential breaking change: Some node attributes are now persistet in your node configuration. This affects the following attributes: + +```json +"config": { + "data_directory": "/var/lib/postgresql/9.4/main", + "hba_file": "/etc/postgresql/9.4/main/pg_hba.conf", + "ident_file": "/etc/postgresql/9.4/main/pg_ident.conf", + "external_pid_file": "/var/run/postgresql/9.4-main.pid", + "unix_socket_directories": "/var/run/postgresql", + "ssl_cert_file": "/etc/ssl/certs/ssl-cert-snakeoil.pem", + "ssl_key_file": "/etc/ssl/private/ssl-cert-snakeoil.key" +} +``` + +- Potential breaking change: Parsing of attributes from node/ environment configuration. It has been reported that setting the `node['postgresql']['client']['packages']` attribute in a cookbook might result in the default version of the postgresql client package being installed alongside the required version. This might affect the server packages as well. +- Correct issues which caused the inability to override installation version defaults +- Correct issues which caused configuration file entries with miss matching version numbers and incorrect file system paths being defined +- Remove method pgdgrepo_rpm_info compile time use of derived attributes case many issues +- Use correct directory path and check for the correct not_if condition to determine if the database has been initialized +- Ensure that correct packages are installed in all scenarios where pg gem is compiled +- Fix errors in configuration files for unix_socket_directory and unix_socket_directories +- Updates to test-kitchen suite configuration +- Added more grey hair to my beard + +## v3.4.24 + +- Corrections to address repositories signed with newer certificates that some distributions have in their default ca-certificates package +- Updates to more accurately determine distributions service init systems adds better support for systemd systems +- Correct how version attribute is evaluated in certain places +- test-kitchen suite configuration corrections +- Opensuse support + +## v3.4.23 -v3.4.23 -------- - Skipping 3.4.22 with Develop branch 3.4.23 to return to releasing cookbook from master on even numbers and develop on odd numbers. -v3.4.21 -------- +## v3.4.21 + - Use more optimistic openssl version constraint - Add Postgresql 9.4 package sources for RHEL platforms - Update testing infrastructure to address bit rot -v3.4.20 -------- -- Revert [#251](https://github.com/hw-cookbooks/postgresql/pull/251), a change which caused the postgresql service to restart every Chef run. +## v3.4.20 + +- Revert [#251](https://github.com/sous-chefs/postgresql/pull/251), a change which caused the postgresql service to restart every Chef run. + +## v3.4.19 -v3.4.19 -------- - node.save could better not be run on every chef run since it causes node.default attributes stored to the node objects to differ during a chef run and when - Missing attribute in docs for yum_pgdg_postgresql - restart postgres service immediately on config change @@ -50,46 +203,46 @@ v3.4.19 - add amazon 2015 - add rhel7 support -v3.4.18 ------- +## v3.4.18 + - Revert changes from #201 with the intention of revisiting these changes as part of the next major version release. - Specify version constraint on openssl cookbook due to an upstream release mishap -v3.4.16 ------- +## v3.4.16 + - Changed hard coded value to attribute #219 - Correction for directory creation under debian, etc. #222 - Fedora 20 yum support #223 - Define version-sensitive attributes in a recipe #201 -v3.4.14 ------- +## v3.4.14 + - Support apt repository for Ubuntu Utopic 14.10 - Do not try and set password on standby hosts -v3.4.12 ------- +## v3.4.12 + - Create configuration templates at the appropriate time - If template is updated restart service changed to default of :delayed - Fix SSL for PostgreSQL versions < 9.2 -v3.4.10 -------- +## v3.4.10 + - correct conditional error created in 3.4.8. -v3.4.8 ------- +## v3.4.8 + - Correct scenario where work_mem could be set to 0 if con is greater than mem Issue #185 - Add Centos7 suites to kitchen configuration -v3.4.6 ------- +## v3.4.6 + - Don't include the pgdg recipes on the wrong machine types - Add missing dir /etc/sysconfig/pgsl for centos7 - CentOS 7 package support -v3.4.4 ------- +## v3.4.4 + - fix packages on SLES11SP2 and higher - [COOK-4737] Add flag to control database user password behavior - add amazon platform rpm info @@ -97,117 +250,117 @@ v3.4.4 - attribute typo correction - correctly check and set max_connections to an integer -v3.4.2 ------- +## v3.4.2 + - Changed the Gem::Installer::ExtensionBuildError to a Mixlib::ShellOut::ShellCommandFailed -v3.4.1 ------- +## v3.4.1 + - Added support for Ubuntu 14.04 and Postgresql 9.3 -- Fix [COOK-3490] https://tickets.opscode.com/browse/COOK-3490 +- Fix [COOK-3490] -v3.4.0 ------- -Updated CONTRIBUTING document. -Refreshed test kitchen configuration. -Merged Pull Requests: 122, 116, 104, 102, 99, 96, 93, 90. +## v3.4.0 + +Updated CONTRIBUTING document. Refreshed test kitchen configuration. Merged Pull Requests: 122, 116, 104, 102, 99, 96, 93, 90. + +## v3.3.4 -v3.3.4 ------- Testing +## v3.3.2 -v3.3.2 ------- - Testing maintainer transfer to Heavywater with Opscode as collaborator +## v3.3.0 -v3.3.0 ------- ### Bug + - **[COOK-3851](https://tickets.opscode.com/browse/COOK-3851)** - Postgresql: reload after config change does not pick up certain configuration changes - **[COOK-3611](https://tickets.opscode.com/browse/COOK-3611)** - unix_socket_directory does not exists in 9.3 - **[COOK-2954](https://tickets.opscode.com/browse/COOK-2954)** - PostgreSQL installation ignores version attribute on CentOS >= 6 +## v3.2.0 -v3.2.0 ------- - [COOK-3717] Pgdg repositories improvements - [COOK-3756] Change postgresql.conf mode from 0600 to 0644 +## v3.1.0 -v3.1.0 ------- ### Improvement + - **[COOK-3685](https://tickets.opscode.com/browse/COOK-3685)** - Upgrade Repo Attributes for Postgresql 9.3 - **[COOK-3597](https://tickets.opscode.com/browse/COOK-3597)** - Fix implementation of `initdb_locale` attribute for RHEL - **[COOK-3566](https://tickets.opscode.com/browse/COOK-3566)** - Give the user's rules more priority than the default ones in pg_hba - **[COOK-3553](https://tickets.opscode.com/browse/COOK-3553)** - Remove automatic `apt-get update` ### Bug + - **[COOK-3611](https://tickets.opscode.com/browse/COOK-3611)** - Remove `unix_socket_directory` (it does not exists in 9.3) - **[COOK-3599](https://tickets.opscode.com/browse/COOK-3599)** - Automatically add PGDG apt repo dependency on PostgreSQL version - **[COOK-3555](https://tickets.opscode.com/browse/COOK-3555)** - Documentation Fix - **[COOK-2383](https://tickets.opscode.com/browse/COOK-2383)** - Update Postgres version in attributes +## v3.0.4 -v3.0.4 ------- ### Bug + - **[COOK-3173](https://tickets.opscode.com/browse/COOK-3173)** - Use :reload instead of :restart on conf changes - **[COOK-2939](https://tickets.opscode.com/browse/COOK-2939)** - Fix RedHat support -v3.0.2 ------- +## v3.0.2 + ### Bug + - [COOK-3076]: postgresql::ruby recipe error when using pgdg repositories -v3.0.0 ------- +## v3.0.0 + This is a backwards-incompatible release because the Pitti PPA is deprecated and the recipe removed, replaced with the PGDG apt repository. ### Bug + - [COOK-2571]: Create helper library for pg extension detection - [COOK-2797]: Contrib extension contianing '-' fails to load. ### Improvement + - [COOK-2387]: Pitti Postgresql PPA is deprecated ### Task + - [COOK-3022]: update baseboxes in .kitchen.yml -v2.4.0 ------- +## v2.4.0 + - [COOK-2163] - Dangerous "assign-postgres-password" in "recipes/server.rb" -- Can lock out dbadmin access - [COOK-2390] - Recipes to auto-generate many postgresql.conf settings, following "initdb" and "pgtune" - [COOK-2435] - Foodcritic fixes for postgresql cookbook - [COOK-2476] - Installation into database of any contrib module extensions listed in a node attribute -v2.2.2 ------- -- [COOK-2232] -Provide PGDG yum repo to install postgresql 9.x on - redhat-derived distributions +## v2.2.2 + +- [COOK-2232] -Provide PGDG yum repo to install postgresql 9.x on redhat-derived distributions + +## v2.2.0 -v2.2.0 ------- - [COOK-2230] - Careful about Debian minor version numbers - [COOK-2231] - Fix support for postgresql 9.x in server_redhat recipe - [COOK-2238] - Postgresql recipe error in password check - [COOK-2176] - PostgreSQL cookbook in Solo mode can cause "NoMethodError: undefined method `[]' for nil:NilClass" - [COOK-2233] - Provide postgresql::contrib recipe to install useful server administration tools -v2.1.0 ------- +## v2.1.0 + - [COOK-1872] - Allow latest PostgreSQL deb packages to be installed - [COOK-1961] - Postgresql config file changes with every Chef run - [COOK-2041] - Postgres cookbook no longer installs on OpenSuSE 11.4 -v2.0.2 ------- +## v2.0.2 + - [COOK-1406] - pg gem compile is unable to find libpq under Chef full stack (omnibus) installation -v2.0.0 ------- +## v2.0.0 + This version is backwards incompatible with previous versions of the cookbook due to use of `platform_family`, and the refactored configuration files using node attributes. See README.md for details on how to modify configuration of PostgreSQL. - [COOK-1508] - fix mixlib shellout error on SUSE @@ -215,11 +368,10 @@ This version is backwards incompatible with previous versions of the cookbook du - [COOK-1779] - Don't run apt-get update and others in ruby recipe if pg is installed - [COOK-1871] - Attribute driven configuration files for PostgreSQL - [COOK-1900] - don't assume ssl on all postgresql 8.4+ installs -- [COOK-1901] - fail a chef-solo run when the postgres password - attribute is not set +- [COOK-1901] - fail a chef-solo run when the postgres password attribute is not set + +## v1.0.0 -v1.0.0 ------- **Important note for this release** This version no longer installs Ruby bindings in the client recipe by default. Use the ruby recipe if you'd like the RubyGem. If you'd like packages for your distribution, use them in your application's specific cookbook/recipe, or modify the client packages attribute. @@ -235,17 +387,17 @@ The following issues are also resolved with this release. - [COOK-1224] - fix undefined variable on Debian - [COOK-1462] - Add attribute for specifying listen address -v0.99.4 ------- +## v0.99.4 + - [COOK-421] - config template is malformed - [COOK-956] - add make package on ubuntu/debian -v0.99.2 ------- +## v0.99.2 + - [COOK-916] - use < (with float) for version comparison. -v0.99.0 ------- +## v0.99.0 + - Better support for Red Hat-family platforms - Integration with database cookbook - Make sure the postgres role is updated with a (secure) password diff --git a/cookbooks/postgresql/CONTRIBUTING.md b/cookbooks/postgresql/CONTRIBUTING.md new file mode 100644 index 0000000..ae6f6df --- /dev/null +++ b/cookbooks/postgresql/CONTRIBUTING.md @@ -0,0 +1,22 @@ +# Contributing + +## Branches + +### `master` branch + +The master branch is the current comitted changes. These changes may not yet be released although we try to release often. + +## Tags + +All releases are tagged in git. To see the releases available to you see the changelog or the tags directly. + + +## Pull requests + +- + +## Issues + +Need to report an issue? Use the github issues: + +- diff --git a/cookbooks/postgresql/README.md b/cookbooks/postgresql/README.md index 847ff32..061a9b0 100644 --- a/cookbooks/postgresql/README.md +++ b/cookbooks/postgresql/README.md @@ -1,443 +1,275 @@ -Description -=========== +# postgresql cookbook + +[![Build Status](https://travis-ci.org/sous-chefs/postgresql.svg?branch=master)](https://travis-ci.org/sous-chefs/postgresql) [![Cookbook Version](https://img.shields.io/cookbook/v/postgresql.svg)](https://supermarket.chef.io/cookbooks/postgresql) Installs and configures PostgreSQL as a client or a server. -Requirements -============ +## Requirements -## Platforms +### Platforms -* Debian, Ubuntu -* Red Hat/CentOS/Scientific (6.0+ required) - "EL6-family" -* Fedora -* SUSE +- Debian 7+ +- Ubuntu 12.04+ +- Red Hat/CentOS/Scientific (6.0+ required) - "EL6-family" +- Fedora +- SLES 12+ +- openSUSE 13+ / openSUSE Leap -Tested on: +### Chef -* Ubuntu 12.04, 14.04, 14.10 -* Red Hat 6.1, Scientific 6.1, CentOS 6.3, 7.0, OpenSuse +- Chef 12.1+ -## Cookbooks +### Cookbooks -Requires Opscode's `openssl` cookbook for secure password generation. +- `compat_resource` +- `openssl` +- `build-essential` -Requires a C compiler and development headers in order to build the -`pg` RubyGem to provide Ruby bindings in the `ruby` recipe. +## Attributes -Opscode's `build-essential` cookbook provides this functionality on -Debian, Ubuntu, and EL6-family. +The following attributes are set based on the platform, see the `attributes/default.rb` file for default values. -While not required, Opscode's `database` cookbook contains resources -and providers that can interact with a PostgreSQL database. This -cookbook is a dependency of database. +- `node['postgresql']['version']` - version of postgresql to manage +- `node['postgresql']['dir']` - home directory of where postgresql data and configuration lives. +- `node['postgresql']['client']['packages']` - An array of package names that should be installed on "client" systems. +- `node['postgresql']['server']['packages']` - An array of package names that should be installed on "server" systems. +- `node['postgresql']['server']['config_change_notify']` - Type of notification triggered when a config file changes. +- `node['postgresql']['contrib']['packages']` - An array of package names that could be installed on "server" systems for useful sysadmin tools. +- `node['postgresql']['enable_pgdg_apt']` - Whether to enable the apt repo by the PostgreSQL Global Development Group, which contains newer versions of PostgreSQL. +- `node['postgresql']['enable_pgdg_yum']` - Whether to enable the yum repo by the PostgreSQL Global Development Group, which contains newer versions of PostgreSQL. +- `node['postgresql']['initdb_locale']` - Sets the default locale for the database cluster. If this attribute is not specified, the locale is inherited from the environment that initdb runs in. Sometimes you must have a system locale that is not what you want for your database cluster, and this attribute addresses that scenario. Valid only for EL-family distros (RedHat/Centos/etc.). -Attributes -========== +The following attributes are generated in `recipe[postgresql::server]`. -The following attributes are set based on the platform, see the -`attributes/default.rb` file for default values. +## Configuration -* `node['postgresql']['version']` - version of postgresql to manage -* `node['postgresql']['dir']` - home directory of where postgresql - data and configuration lives. +The `postgresql.conf` and `pg_hba.conf` files are dynamically generated from attributes. Each key in `node['postgresql']['config']` is a postgresql configuration directive, and will be rendered in the config file. For example, the attribute: -* `node['postgresql']['client']['packages']` - An array of package names - that should be installed on "client" systems. -* `node['postgresql']['server']['packages']` - An array of package names - that should be installed on "server" systems. -* `node['postgresql']['server']['config_change_notify']` - Type of - notification triggered when a config file changes. -* `node['postgresql']['contrib']['packages']` - An array of package names - that could be installed on "server" systems for useful sysadmin tools. - -* `node['postgresql']['enable_pgdg_apt']` - Whether to enable the apt repo - by the PostgreSQL Global Development Group, which contains newer versions - of PostgreSQL. - -* `node['postgresql']['enable_pgdg_yum']` - Whether to enable the yum repo - by the PostgreSQL Global Development Group, which contains newer versions - of PostgreSQL. - -* `node['postgresql']['initdb_locale']` - Sets the default locale for the - database cluster. If this attribute is not specified, the locale is - inherited from the environment that initdb runs in. Sometimes you must - have a system locale that is not what you want for your database cluster, - and this attribute addresses that scenario. Valid only for EL-family - distros (RedHat/Centos/etc.). - -The following attributes are generated in -`recipe[postgresql::server]`. - -Configuration -------------- - -The `postgresql.conf` and `pg_hba.conf` files are dynamically -generated from attributes. Each key in `node['postgresql']['config']` -is a postgresql configuration directive, and will be rendered in the -config file. For example, the attribute: - - node['postgresql']['config']['listen_addresses'] = 'localhost' +```ruby +node['postgresql']['config']['listen_addresses'] = 'localhost' +``` Will result in the following line in the `postgresql.conf` file: - listen_addresses = 'localhost' +```ruby +listen_addresses = 'localhost' +``` -The attributes file contains default values for Debian and RHEL -platform families (per the `node['platform_family']`). These defaults -have disparity between the platforms because they were originally -extracted from the postgresql.conf files in the previous version of -this cookbook, which differed in their default config. The resulting -configuration files will be the same as before, but the content will -be dynamically rendered from the attributes. The helpful commentary -will no longer be present. You should consult the PostgreSQL -documentation for specific configuration details. +The attributes file contains default values for Debian and RHEL platform families (per the `node['platform_family']`). These defaults have disparity between the platforms because they were originally extracted from the postgresql.conf files in the previous version of this cookbook, which differed in their default config. The resulting configuration files will be the same as before, but the content will be dynamically rendered from the attributes. The helpful commentary will no longer be present. You should consult the PostgreSQL documentation for specific configuration details. -See __Recipes__ `config_initdb` and `config_pgtune` below to -auto-generate many postgresql.conf settings. +See **Recipes** `config_initdb` and `config_pgtune` below to auto-generate many postgresql.conf settings. -For values that are "on" or "off", they should be specified as literal -`true` or `false`. String values will be used with single quotes. Any -configuration option set to the literal `nil` will be skipped -entirely. All other values (e.g., numeric literals) will be used as -is. So for example: +For values that are "on" or "off", they should be specified as literal `true` or `false`. String values will be used with single quotes. Any configuration option set to the literal `nil` will be skipped entirely. All other values (e.g., numeric literals) will be used as is. So for example: - node.default['postgresql']['config']['logging_collector'] = true - node.default['postgresql']['config']['datestyle'] = 'iso, mdy' - node.default['postgresql']['config']['ident_file'] = nil - node.default['postgresql']['config']['port'] = 5432 +```ruby +node.default['postgresql']['config']['logging_collector'] = true +node.default['postgresql']['config']['datestyle'] = 'iso, mdy' +node.default['postgresql']['config']['ident_file'] = nil +node.default['postgresql']['config']['port'] = 5432 +``` Will result in the following config lines: - logging_collector = 'on' - datestyle = 'iso,mdy' - port = 5432 +```ruby +logging_collector = 'on' +datestyle = 'iso,mdy' +port = 5432 +``` (no line printed for `ident_file` as it is `nil`) -Note that the `unix_socket_directory` configuration was renamed to -`unix_socket_directories` in Postgres 9.3 so make sure to use the -`node['postgresql']['unix_socket_directories']` attribute instead of -`node['postgresql']['unix_socket_directory']`. +Note that the `unix_socket_directory` configuration was renamed to `unix_socket_directories` in Postgres 9.3 so make sure to use the `node['postgresql']['unix_socket_directories']` attribute instead of `node['postgresql']['unix_socket_directory']`. -The `pg_hba.conf` file is dynamically generated from the -`node['postgresql']['pg_hba']` attribute. This attribute must be an -array of hashes, each hash containing the authorization data. As it is -an array, you can append to it in your own recipes. The hash keys in -the array must be symbols. Each hash will be written as a line in -`pg_hba.conf`. For example, this entry from -`node['postgresql']['pg_hba']`: +The `pg_hba.conf` file is dynamically generated from the `node['postgresql']['pg_hba']` attribute. This attribute must be an array of hashes, each hash containing the authorization data. As it is an array, you can append to it in your own recipes. The hash keys in the array must be symbols. Each hash will be written as a line in `pg_hba.conf`. For example, this entry from `node['postgresql']['pg_hba']`: - [{:comment => '# Optional comment', - :type => 'local', :db => 'all', :user => 'postgres', :addr => nil, :method => 'md5'}] +``` +[{:comment => '# Optional comment', +:type => 'local', :db => 'all', :user => 'postgres', :addr => nil, :method => 'md5'}] +``` Will result in the following line in `pg_hba.conf`: - # Optional comment - local all postgres md5 +``` +# Optional comment +local all postgres md5 +``` -Use `nil` if the CIDR-ADDRESS should be empty (as above). -Don't provide a comment if none is desired in the `pg_hba.conf` file. +Use `nil` if the CIDR-ADDRESS should be empty (as above). Don't provide a comment if none is desired in the `pg_hba.conf` file. -Note that the following authorization rule is supplied automatically by -the cookbook template. The cookbook needs this to execute SQL in the -PostgreSQL server without supplying the clear-text password (which isn't -known by the cookbook). Therefore, your `node['postgresql']['pg_hba']` -attributes don't need to specify this authorization rule: +Note that the following authorization rule is supplied automatically by the cookbook template. The cookbook needs this to execute SQL in the PostgreSQL server without supplying the clear-text password (which isn't known by the cookbook). Therefore, your `node['postgresql']['pg_hba']` attributes don't need to specify this authorization rule: - # "local" is for Unix domain socket connections only - local all all ident +``` +# "local" is for Unix domain socket connections only +local all all ident +``` -(By the way, the template uses `peer` instead of `ident` for PostgreSQL-9.1 -and above, which has the same effect.) +(By the way, the template uses `peer` instead of `ident` for PostgreSQL-9.1 and above, which has the same effect.) -Recipes -======= +## Recipes -default -------- +### default Includes the client recipe. -client ------- +### client -Installs the packages defined in the -`node['postgresql']['client']['packages']` attribute. +Installs the packages defined in the `node['postgresql']['client']['packages']` attribute. -ruby ----- +### ruby -Install the `pg` gem under Chef's Ruby environment so it can be used -in other recipes. The build-essential packages and postgresql client -packages will be installed during the compile phase, so that the -native extensions of `pg` can be compiled. +Install the `pg` gem under Chef's Ruby environment so it can be used in other recipes. The build-essential packages and postgresql client packages will be installed during the compile phase, so that the native extensions of `pg` can be compiled. -server ------- +### server -Includes the `server_debian` or `server_redhat` recipe to get the -appropriate server packages installed and service managed. Also -manages the configuration for the server: +Includes the `server_debian` or `server_redhat` recipe to get the appropriate server packages installed and service managed. Also manages the configuration for the server: -* generates a strong default password (via `openssl`) for `postgres` -* sets the password for postgres -* manages the `postgresql.conf` file. -* manages the `pg_hba.conf` file. +- generates a strong default password (via `openssl`) for `postgres` +- sets the password for postgres +- manages the `postgresql.conf` file. +- manages the `pg_hba.conf` file. -server\_debian --------------- +### config_initdb -Installs the postgresql server packages and sets up the service. You -should include the `postgresql::server` recipe, which will include -this on Debian platforms. +Takes locale and timezone settings from the system configuration. This recipe creates `node.default['postgresql']['config']` attributes that conform to the system's locale and timezone. In addition, this recipe creates the same error reporting and logging settings that `initdb` provided: a rotation of 7 days of log files named postgresql-Mon.log, etc. -server\_redhat --------------- +The default attributes created by this recipe are easy to override with normal attributes because of Chef attribute precedence. For example, suppose a DBA wanted to keep log files indefinitely, rolling over daily or when growing to 10MB. The Chef installation could include the `postgresql::config_initdb` recipe for the locale and timezone settings, but customize the logging settings with these node JSON attributes: -Manages the postgres user and group (with UID/GID 26, per RHEL package -conventions), installs the postgresql server packages, initializes the -database, and manages the postgresql service. You should include the -`postgresql::server` recipe, which will include this on RHEL/Fedora -platforms. +```javascript +"postgresql": { + "config": { + "log_rotation_age": "1d", + "log_rotation_size": "10MB", + "log_filename": "postgresql-%Y-%m-%d_%H%M%S.log" + } +} +``` -config\_initdb --------------- +Credits: This `postgresql::config_initdb` recipe is based on algorithms in the [source code](http://doxygen.postgresql.org/initdb_8c_source.html) for the PostgreSQL `initdb` utility. -Takes locale and timezone settings from the system configuration. -This recipe creates `node.default['postgresql']['config']` attributes -that conform to the system's locale and timezone. In addition, this -recipe creates the same error reporting and logging settings that -`initdb` provided: a rotation of 7 days of log files named -postgresql-Mon.log, etc. +### config_pgtune -The default attributes created by this recipe are easy to override with -normal attributes because of Chef attribute precedence. For example, -suppose a DBA wanted to keep log files indefinitely, rolling over daily -or when growing to 10MB. The Chef installation could include the -`postgresql::config_initdb` recipe for the locale and timezone settings, -but customize the logging settings with these node JSON attributes: +Performance tuning. Takes the wimpy default postgresql.conf and expands the database server to be as powerful as the hardware it's being deployed on. This recipe creates a baseline configuration of `node.default['postgresql']['config']` attributes in the right general range for a dedicated Postgresql system. Most installations won't need additional performance tuning. - "postgresql": { - "config": { - "log_rotation_age": "1d", - "log_rotation_size": "10MB", - "log_filename": "postgresql-%Y-%m-%d_%H%M%S.log" - } +The only decision you need to make is to choose a `db_type` from the following database workloads. (See the recipe code comments for more detailed descriptions.) + +- "dw" -- Data Warehouse +- "oltp" -- Online Transaction Processing +- "web" -- Web Application +- "mixed" -- Mixed DW and OLTP characteristics +- "desktop" -- Not a dedicated database + +This recipe uses a performance model with three input parameters. These node attributes are completely optional, but it is obviously important to choose the `db_type` correctly: + +- `node['postgresql']['config_pgtune']['db_type']` -- Specifies database type from the list of five choices above. If not specified, the default is "mixed". + +- `node['postgresql']['config_pgtune']['max_connections']` -- Specifies maximum number of connections expected. If not specified, it depends on database type: "web":200, "oltp":300, "dw":20, "mixed":80, "desktop":5 + +- `node['postgresql']['config_pgtune']['total_memory']` -- Specifies total system memory in kB. (E.g., "49416564kB".) If not specified, it will be taken from Ohai automatic attributes. This could be used to tune a system that isn't a dedicated database. + +The default attributes created by this recipe are easy to override with normal attributes because of Chef attribute precedence. For example, if you are running application benchmarks to try different buffer cache sizes, you would experiment with this node JSON attribute: + +```javascript +"postgresql": { + "config": { + "shared_buffers": "3GB" + } +} +``` + +Note that the recipe uses `max_connections` in its computations. If you want to override that setting, you should specify `node['postgresql']['config_pgtune']['max_connections']` instead of `node['postgresql']['config']['max_connections']`. + +Credits: This `postgresql::config_pgtune` recipe is based on the [pgtune python script](https://github.com/gregs1104/pgtune) developed by [Greg Smith](http://notemagnet.blogspot.com/2008/11/automating-initial-postgresqlconf.html) and [other pgsql-hackers](http://www.postgresql.org/message-id/491C6CDC.8090506@agliodbs.com). + +### contrib + +Installs the packages defined in the `node['postgresql']['contrib']['packages']` attribute. The contrib directory of the PostgreSQL distribution includes porting tools, analysis utilities, and plug-in features that database engineers often require. Some (like `pgbench`) are executable. Others (like `pg_buffercache`) would need to be installed into the database. + +Also installs any contrib module extensions defined in the `node['postgresql']['contrib']['extensions']` attribute. These will be available in any subsequently created databases in the cluster, because they will be installed into the `template1` database using the `CREATE EXTENSION` command. For example, it is often necessary/helpful for problem troubleshooting and maintenance planning to install the views and functions in these [standard instrumentation extensions] ([http://www.postgresql.org/message-id/flat/4DC32600.6080900@pgexperts.com#4DD3D6C6.5060006@2ndquadrant.com](mailto:http://www.postgresql.org/message-id/flat/4DC32600.6080900@pgexperts.com#4DD3D6C6.5060006@2ndquadrant.com)): + +```ruby +node['postgresql']['contrib']['extensions'] = [ + "pageinspect", + "pg_buffercache", + "pg_freespacemap", + "pgrowlocks", + "pg_stat_statements", + "pgstattuple" +] +``` + +Note that the `pg_stat_statements` view only works if `postgresql.conf` loads its shared library, which can be done with this node attribute: + +```ruby +node['postgresql']['config']['shared_preload_libraries'] = 'pg_stat_statements' +``` + +If using `shared_preload_libraries` in combination with the `contrib` recipe, make sure that the `contrib` recipe is called before the `server` recipe (to ensure the dependencies are installed and setup in order). + +### apt_pgdg_postgresql + +Enables the PostgreSQL Global Development Group yum repository maintained by Devrim Gündüz for updated PostgreSQL packages. (The PGDG is the groups that develops PostgreSQL.) Automatically included if the `node['postgresql']['enable_pgdg_apt']` attribute is true. Also set the `node['postgresql']['client']['packages']` and `node['postgresql']['server]['packages']` to the list of packages to use from this repository, and set the `node['postgresql']['version']` attribute to the version to use (e.g., "9.2"). + +### yum_pgdg_postgresql + +Enables the PostgreSQL Global Development Group yum repository maintained by Devrim Gündüz for updated PostgreSQL packages. (The PGDG is the groups that develops PostgreSQL.) Automatically included if the `node['postgresql']['enable_pgdg_yum']` attribute is true. Also use `override_attributes` to set a number of values that will need to have embedded version numbers. For example: + +```ruby +node['postgresql']['enable_pgdg_yum'] = true +node['postgresql']['version'] = "9.4" +node['postgresql']['dir'] = "/var/lib/pgsql/9.4/data" +node['postgresql']['config']['data_directory'] = node['postgresql']['dir'] +node['postgresql']['client']['packages'] = ["postgresql94", "postgresql94-devel"] +node['postgresql']['server']['packages'] = ["postgresql94-server"] +node['postgresql']['server']['service_name'] = "postgresql-9.4" +node['postgresql']['contrib']['packages'] = ["postgresql94-contrib"] +node['postgresql']['setup_script'] = "postgresql94-setup" +``` + +You may set `node['postgresql']['pgdg']['repo_rpm_url']` attributes to pick up recent [PGDG repo packages](http://yum.postgresql.org/repopackages.php). + +## Usage + +On systems that need to connect to a PostgreSQL database, add to a run list `recipe[postgresql]` or `recipe[postgresql::client]`. + +On systems that should be PostgreSQL servers, use `recipe[postgresql::server]` on a run list. This recipe does set a password for the `postgres` user. If you're using `chef server`, if the attribute `node['postgresql']['password']['postgres']` is not found, the recipe generates a random password and performs a node.save. (TODO: This is broken, as it disables the password.) If you're using `chef-solo`, you'll need to set the attribute `node['postgresql']['password']['postgres']` in your node's `json_attribs` file or in a role. + +On Debian family systems, SSL will be enabled, as the packages on Debian/Ubuntu also generate the SSL certificates. If you use another platform and wish to use SSL in postgresql, then generate your SSL certificates and distribute them in your own cookbook, and set the `node['postgresql']['config']['ssl']` attribute to true in your role/cookboook/node. + +On server systems, the postgres server is restarted when a configuration file changes. This can be changed to reload only by setting the following attribute: + +```ruby +node['postgresql']['server']['config_change_notify'] = :reload +``` + +## Chef Solo Note + +The following node attribute is stored on the Chef Server when using `chef-client`. Because `chef-solo` does not connect to a server or save the node object at all, to have the password persist across `chef-solo` runs, you must specify them in the `json_attribs` file used. For Example: + +``` +{ + "postgresql": { + "password": { + "postgres": "iloverandompasswordsbutthiswilldo" } + }, + "run_list": ["recipe[postgresql::server]"] +} +``` -Credits: This `postgresql::config_initdb` recipe is based on algorithms -in the [source code](http://doxygen.postgresql.org/initdb_8c_source.html) -for the PostgreSQL `initdb` utility. +That should actually be the "encrypted password" instead of cleartext, so you should generate it as an md5 hash using the PostgreSQL algorithm. -config\_pgtune --------------- +- You could copy the md5-hashed password from an existing postgres database if you have `postgres` access and want to use the same password:
+ `select * from pg_shadow where usename='postgres';` +- You can run this from any postgres database session to use a new password:
+ `select 'md5'||md5('iloverandompasswordsbutthiswilldo'||'postgres');` +- You can run this from a linux commandline:
+ `echo -n 'iloverandompasswordsbutthiswilldo''postgres' | openssl md5 | sed -e 's/.* /md5/'` -Performance tuning. -Takes the wimpy default postgresql.conf and expands the database server -to be as powerful as the hardware it's being deployed on. This recipe -creates a baseline configuration of `node.default['postgresql']['config']` -attributes in the right general range for a dedicated Postgresql system. -Most installations won't need additional performance tuning. +## License -The only decision you need to make is to choose a `db_type` from the -following database workloads. (See the recipe code comments for more -detailed descriptions.) - - * "dw" -- Data Warehouse - * "oltp" -- Online Transaction Processing - * "web" -- Web Application - * "mixed" -- Mixed DW and OLTP characteristics - * "desktop" -- Not a dedicated database - -This recipe uses a performance model with three input parameters. -These node attributes are completely optional, but it is obviously -important to choose the `db_type` correctly: - - * `node['postgresql']['config_pgtune']['db_type']` -- - Specifies database type from the list of five choices above. - If not specified, the default is "mixed". - - * `node['postgresql']['config_pgtune']['max_connections']` -- - Specifies maximum number of connections expected. - If not specified, it depends on database type: - "web":200, "oltp":300, "dw":20, "mixed":80, "desktop":5 - - * `node['postgresql']['config_pgtune']['total_memory']` -- - Specifies total system memory in kB. (E.g., "49416564kB".) - If not specified, it will be taken from Ohai automatic attributes. - This could be used to tune a system that isn't a dedicated database. - -The default attributes created by this recipe are easy to override with -normal attributes because of Chef attribute precedence. For example, if -you are running application benchmarks to try different buffer cache -sizes, you would experiment with this node JSON attribute: - - "postgresql": { - "config": { - "shared_buffers": "3GB" - } - } - -Note that the recipe uses `max_connections` in its computations. If -you want to override that setting, you should specify -`node['postgresql']['config_pgtune']['max_connections']` instead of -`node['postgresql']['config']['max_connections']`. - -Credits: This `postgresql::config_pgtune` recipe is based on the -[pgtune python script](https://github.com/gregs1104/pgtune) -developed by -[Greg Smith](http://notemagnet.blogspot.com/2008/11/automating-initial-postgresqlconf.html) -and -[other pgsql-hackers](http://www.postgresql.org/message-id/491C6CDC.8090506@agliodbs.com). - -contrib -------- - -Installs the packages defined in the -`node['postgresql']['contrib']['packages']` attribute. The contrib -directory of the PostgreSQL distribution includes porting tools, -analysis utilities, and plug-in features that database engineers often -require. Some (like `pgbench`) are executable. Others (like -`pg_buffercache`) would need to be installed into the database. - -Also installs any contrib module extensions defined in the -`node['postgresql']['contrib']['extensions']` attribute. These will be -available in any subsequently created databases in the cluster, because -they will be installed into the `template1` database using the -`CREATE EXTENSION` command. For example, it is often necessary/helpful -for problem troubleshooting and maintenance planning to install the -views and functions in these [standard instrumentation extensions] -(http://www.postgresql.org/message-id/flat/4DC32600.6080900@pgexperts.com#4DD3D6C6.5060006@2ndquadrant.com): - - node['postgresql']['contrib']['extensions'] = [ - "pageinspect", - "pg_buffercache", - "pg_freespacemap", - "pgrowlocks", - "pg_stat_statements", - "pgstattuple" - ] - -Note that the `pg_stat_statements` view only works if `postgresql.conf` -loads its shared library, which can be done with this node attribute: - - node['postgresql']['config']['shared_preload_libraries'] = 'pg_stat_statements' - -If using `shared_preload_libraries` in combination with the `contrib` recipe, -make sure that the `contrib` recipe is called before the `server` recipe (to -ensure the dependencies are installed and setup in order). - -apt\_pgdg\_postgresql ----------------------- - -Enables the PostgreSQL Global Development Group yum repository -maintained by Devrim Gündüz for updated PostgreSQL packages. -(The PGDG is the groups that develops PostgreSQL.) -Automatically included if the `node['postgresql']['enable_pgdg_apt']` -attribute is true. Also set the -`node['postgresql']['client']['packages']` and -`node['postgresql']['server]['packages']` to the list of packages to -use from this repository, and set the `node['postgresql']['version']` -attribute to the version to use (e.g., "9.2"). - -yum\_pgdg\_postgresql ---------------------- - -Enables the PostgreSQL Global Development Group yum repository -maintained by Devrim Gündüz for updated PostgreSQL packages. -(The PGDG is the groups that develops PostgreSQL.) -Automatically included if the `node['postgresql']['enable_pgdg_yum']` -attribute is true. Also use `override_attributes` to set a number of -values that will need to have embedded version numbers. For example: - - node['postgresql']['enable_pgdg_yum'] = true - node['postgresql']['version'] = "9.2" - node['postgresql']['dir'] = "/var/lib/pgsql/9.2/data" - node['postgresql']['config']['data_directory'] = node['postgresql']['dir'] - node['postgresql']['client']['packages'] = ["postgresql92", "postgresql92-devel"] - node['postgresql']['server']['packages'] = ["postgresql92-server"] - node['postgresql']['server']['service_name'] = "postgresql-9.2" - node['postgresql']['contrib']['packages'] = ["postgresql92-contrib"] - -You may set `node['postgresql']['pgdg']['repo_rpm_url']` attributes -to pick up recent [PGDG repo packages](http://yum.postgresql.org/repopackages.php). - -Resources/Providers -=================== - -See the [database](http://community.opscode.com/cookbooks/database) -for resources and providers that can be used for managing PostgreSQL -users and databases. - -Usage -===== - -On systems that need to connect to a PostgreSQL database, add to a run -list `recipe[postgresql]` or `recipe[postgresql::client]`. - -On systems that should be PostgreSQL servers, use -`recipe[postgresql::server]` on a run list. This recipe does set a -password for the `postgres` user. -If you're using `chef server`, if the attribute -`node['postgresql']['password']['postgres']` is not found, -the recipe generates a random password and performs a node.save. -(TODO: This is broken, as it disables the password.) -If you're using `chef-solo`, you'll need -to set the attribute `node['postgresql']['password']['postgres']` in -your node's `json_attribs` file or in a role. - -On Debian family systems, SSL will be enabled, as the packages on -Debian/Ubuntu also generate the SSL certificates. If you use another -platform and wish to use SSL in postgresql, then generate your SSL -certificates and distribute them in your own cookbook, and set the -`node['postgresql']['config']['ssl']` attribute to true in your -role/cookboook/node. - -On server systems, the postgres server is restarted when a configuration -file changes. This can be changed to reload only by setting the -following attribute: - - node['postgresql']['server']['config_change_notify'] = :reload - -Chef Solo Note -============== - -The following node attribute is stored on the Chef Server when using -`chef-client`. Because `chef-solo` does not connect to a server or -save the node object at all, to have the password persist across -`chef-solo` runs, you must specify them in the `json_attribs` file -used. For Example: - - { - "postgresql": { - "password": { - "postgres": "iloverandompasswordsbutthiswilldo" - } - }, - "run_list": ["recipe[postgresql::server]"] - } - -That should actually be the "encrypted password" instead of cleartext, -so you should generate it as an md5 hash using the PostgreSQL algorithm. - -* You could copy the md5-hashed password from an existing postgres -database if you have `postgres` access and want to use the same password:
-`select * from pg_shadow where usename='postgres';` -* You can run this from any postgres database session to use a new password:
-`select 'md5'||md5('iloverandompasswordsbutthiswilldo'||'postgres');` -* You can run this from a linux commandline:
-`echo -n 'iloverandompasswordsbutthiswilldo''postgres' | openssl md5 | sed -e 's/.* /md5/'` - -License and Author -================== - -- Author:: Joshua Timberman () -- Author:: Lamont Granquist () -- Author:: Chris Roberts () -- Author:: David Crane () -- Author:: Aaron Baer () +Copyright 2010-2016, Chef Software, Inc. +```text 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 @@ -449,3 +281,4 @@ 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/cookbooks/postgresql/attributes/default.rb b/cookbooks/postgresql/attributes/default.rb index 345ed7d..27d2c7f 100644 --- a/cookbooks/postgresql/attributes/default.rb +++ b/cookbooks/postgresql/attributes/default.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Attributes:: postgresql # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,6 +18,7 @@ default['postgresql']['enable_pgdg_apt'] = false default['postgresql']['enable_pgdg_yum'] = false +default['postgresql']['use_pgdg_packages'] = false default['postgresql']['server']['config_change_notify'] = :restart default['postgresql']['assign_postgres_password'] = true @@ -25,17 +27,16 @@ default['postgresql']['assign_postgres_password'] = true default['postgresql']['database_name'] = 'template1' # Sets OS init system (upstart, systemd, ...), instead of relying on Ohai -default['postgresql']['server']['init_package'] = case node['platform'] +default['postgresql']['server']['init_package'] = + case node['platform'] when 'debian' - case - when node['platform_version'].to_f < 7.0 + if node['platform_version'].to_f < 7.0 'sysv' else 'systemd' end when 'ubuntu' - case - when node['platform_version'].to_f < 15.04 + if node['platform_version'].to_f < 15.04 'upstart' else 'systemd' @@ -43,195 +44,154 @@ default['postgresql']['server']['init_package'] = case node['platform'] when 'amazon' 'upstart' when 'redhat', 'centos', 'scientific', 'oracle' - case - when node['platform_version'].to_f < 6.0 + if node['platform_version'].to_i < 7 'sysv' - when node['platform_version'].to_f < 7.0 - 'upstart' else 'systemd' end when 'fedora' - case - when node['platform_version'].to_f < 15 - 'upstart' - else - 'systemd' - end - when 'opensuse' - case - when node['platform_version'].to_f < 13 - 'sysv' - else - 'systemd' - end + 'systemd' + when 'opensuse', 'opensuseleap' + 'systemd' else 'upstart' end case node['platform'] -when "debian" - - case - when node['platform_version'].to_f < 6.0 # All 5.X - default['postgresql']['version'] = "8.3" - default['postgresql']['dir'] = "/etc/postgresql/8.3/main" - default['postgresql']['client']['packages'] = ["postgresql-client-8.3","libpq-dev"] - default['postgresql']['server']['packages'] = ["postgresql-8.3"] - default['postgresql']['contrib']['packages'] = ["postgresql-contrib-8.3"] - when node['platform_version'].to_f < 7.0 # All 6.X - default['postgresql']['version'] = "8.4" - default['postgresql']['dir'] = "/etc/postgresql/8.4/main" - default['postgresql']['client']['packages'] = ["postgresql-client-8.4","libpq-dev"] - default['postgresql']['server']['packages'] = ["postgresql-8.4"] - default['postgresql']['contrib']['packages'] = ["postgresql-contrib-8.4"] - when node['platform_version'].to_f < 8.0 # All 7.X - default['postgresql']['version'] = "9.1" - default['postgresql']['dir'] = "/etc/postgresql/9.1/main" - default['postgresql']['client']['packages'] = ["postgresql-client-9.1","libpq-dev"] - default['postgresql']['server']['packages'] = ["postgresql-9.1"] - default['postgresql']['contrib']['packages'] = ["postgresql-contrib-9.1"] - else - default['postgresql']['version'] = "9.4" - default['postgresql']['dir'] = "/etc/postgresql/9.4/main" - default['postgresql']['client']['packages'] = ["postgresql-client-9.4","libpq-dev"] - default['postgresql']['server']['packages'] = ["postgresql-9.4"] - default['postgresql']['contrib']['packages'] = ["postgresql-contrib-9.4"] +when 'debian' + if node['platform_version'].to_i == 7 + default['postgresql']['version'] = '9.1' + default['postgresql']['dir'] = '/etc/postgresql/9.1/main' + default['postgresql']['client']['packages'] = ['postgresql-client-9.1', 'libpq-dev'] + default['postgresql']['server']['packages'] = ['postgresql-9.1'] + default['postgresql']['contrib']['packages'] = ['postgresql-contrib-9.1'] + else # 8+ + default['postgresql']['version'] = '9.4' + default['postgresql']['dir'] = '/etc/postgresql/9.4/main' + default['postgresql']['client']['packages'] = ['postgresql-client-9.4', 'libpq-dev'] + default['postgresql']['server']['packages'] = ['postgresql-9.4'] + default['postgresql']['contrib']['packages'] = ['postgresql-contrib-9.4'] end - case - when node['platform_version'].to_f < 6.0 # All 5.X - default['postgresql']['server']['service_name'] = "postgresql-8.3" + default['postgresql']['server']['service_name'] = 'postgresql' + +when 'ubuntu' + + if node['platform_version'].to_f <= 13.10 + default['postgresql']['version'] = '9.1' + default['postgresql']['dir'] = '/etc/postgresql/9.1/main' + default['postgresql']['server']['service_name'] = 'postgresql' + default['postgresql']['client']['packages'] = ['postgresql-client-9.1', 'libpq-dev'] + default['postgresql']['server']['packages'] = ['postgresql-9.1'] + default['postgresql']['contrib']['packages'] = ['postgresql-contrib-9.1'] + elsif node['platform_version'].to_f <= 14.04 + default['postgresql']['version'] = '9.3' + default['postgresql']['dir'] = '/etc/postgresql/9.3/main' + default['postgresql']['server']['service_name'] = 'postgresql' + default['postgresql']['client']['packages'] = ['postgresql-client-9.3', 'libpq-dev'] + default['postgresql']['server']['packages'] = ['postgresql-9.3'] + default['postgresql']['contrib']['packages'] = ['postgresql-contrib-9.3'] + elsif node['platform_version'].to_f <= 15.10 + default['postgresql']['version'] = '9.4' + default['postgresql']['dir'] = '/etc/postgresql/9.4/main' + default['postgresql']['server']['service_name'] = 'postgresql' + default['postgresql']['client']['packages'] = ['postgresql-client-9.4', 'libpq-dev'] + default['postgresql']['server']['packages'] = ['postgresql-9.4'] + default['postgresql']['contrib']['packages'] = ['postgresql-contrib-9.4'] else - default['postgresql']['server']['service_name'] = "postgresql" + default['postgresql']['version'] = '9.5' + default['postgresql']['dir'] = '/etc/postgresql/9.5/main' + default['postgresql']['server']['service_name'] = 'postgresql' + default['postgresql']['client']['packages'] = ['postgresql-client-9.5', 'libpq-dev'] + default['postgresql']['server']['packages'] = ['postgresql-9.5'] + default['postgresql']['contrib']['packages'] = ['postgresql-contrib-9.5'] end +when 'fedora' -when "ubuntu" + default['postgresql']['version'] = '9.5' + default['postgresql']['setup_script'] = 'postgresql-setup' + default['postgresql']['dir'] = '/var/lib/pgsql/data' + default['postgresql']['client']['packages'] = %w(postgresql-devel postgresql-contrib) + default['postgresql']['server']['packages'] = %w(postgresql-server) + default['postgresql']['contrib']['packages'] = %w(postgresql-contrib) + default['postgresql']['server']['service_name'] = 'postgresql' + default['postgresql']['uid'] = '26' + default['postgresql']['gid'] = '26' - case - when node['platform_version'].to_f <= 9.04 - default['postgresql']['version'] = "8.3" - default['postgresql']['dir'] = "/etc/postgresql/8.3/main" - default['postgresql']['server']['service_name'] = "postgresql-8.3" - default['postgresql']['client']['packages'] = ["postgresql-client-8.3","libpq-dev"] - default['postgresql']['server']['packages'] = ["postgresql-8.3"] - default['postgresql']['contrib']['packages'] = ["postgresql-contrib-8.3"] - when node['platform_version'].to_f <= 11.04 - default['postgresql']['version'] = "8.4" - default['postgresql']['dir'] = "/etc/postgresql/8.4/main" - default['postgresql']['server']['service_name'] = "postgresql" - default['postgresql']['client']['packages'] = ["postgresql-client-8.4","libpq-dev"] - default['postgresql']['server']['packages'] = ["postgresql-8.4"] - default['postgresql']['contrib']['packages'] = ["postgresql-contrib-8.4"] - when node['platform_version'].to_f <= 13.10 - default['postgresql']['version'] = "9.1" - default['postgresql']['dir'] = "/etc/postgresql/9.1/main" - default['postgresql']['server']['service_name'] = "postgresql" - default['postgresql']['client']['packages'] = ["postgresql-client-9.1","libpq-dev"] - default['postgresql']['server']['packages'] = ["postgresql-9.1"] - default['postgresql']['contrib']['packages'] = ["postgresql-contrib-9.1"] - else - default['postgresql']['version'] = "9.3" - default['postgresql']['dir'] = "/etc/postgresql/9.3/main" - default['postgresql']['server']['service_name'] = "postgresql" - default['postgresql']['client']['packages'] = ["postgresql-client-9.3","libpq-dev"] - default['postgresql']['server']['packages'] = ["postgresql-9.3"] - default['postgresql']['contrib']['packages'] = ["postgresql-contrib-9.3"] +when 'amazon' + + if node['platform_version'].to_f >= 2015.03 + default['postgresql']['version'] = '9.2' + default['postgresql']['dir'] = '/var/lib/pgsql9/data' end -when "fedora" + default['postgresql']['client']['packages'] = %w(postgresql-devel) + default['postgresql']['server']['packages'] = %w(postgresql-server) + default['postgresql']['contrib']['packages'] = %w(postgresql-contrib) + default['postgresql']['server']['service_name'] = 'postgresql' + default['postgresql']['uid'] = '26' + default['postgresql']['gid'] = '26' - if node['platform_version'].to_f <= 12 - default['postgresql']['version'] = "8.3" - else - default['postgresql']['version'] = "8.4" - end +when 'redhat', 'centos', 'scientific', 'oracle' - default['postgresql']['setup_script'] = "postgresql-setup" + default['postgresql']['version'] = '8.4' - default['postgresql']['dir'] = "/var/lib/pgsql/data" - default['postgresql']['client']['packages'] = %w{postgresql-devel} - default['postgresql']['server']['packages'] = %w{postgresql-server} - default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} - default['postgresql']['server']['service_name'] = "postgresql" + default['postgresql']['client']['packages'] = 'postgresql84-devel' + default['postgresql']['server']['packages'] = ['postgresql84-server'] + default['postgresql']['contrib']['packages'] = ['postgresql84-contrib'] -when "amazon" - - if node['platform_version'].to_f == 2012.03 - default['postgresql']['version'] = "9.0" - default['postgresql']['dir'] = "/var/lib/pgsql9/data" - elsif node['platform_version'].to_f >= 2015.03 - default['postgresql']['version'] = "9.2" - default['postgresql']['dir'] = "/var/lib/pgsql9/data" - else - default['postgresql']['version'] = "8.4" - default['postgresql']['dir'] = "/var/lib/pgsql/data" - end - - default['postgresql']['client']['packages'] = %w{postgresql-devel} - default['postgresql']['server']['packages'] = %w{postgresql-server} - default['postgresql']['contrib']['packages'] = %w{postgresql-contrib} - default['postgresql']['server']['service_name'] = "postgresql" - -when "redhat", "centos", "scientific", "oracle" - - default['postgresql']['version'] = "8.4" - - default['postgresql']['client']['packages'] = ["postgresql84-devel"] - default['postgresql']['server']['packages'] = ["postgresql84-server"] - default['postgresql']['contrib']['packages'] = ["postgresql84-contrib"] - - default['postgresql']['setup_script'] = "postgresql-setup" - default['postgresql']['server']['service_name'] = "postgresql" + default['postgresql']['setup_script'] = 'postgresql-setup' + default['postgresql']['server']['service_name'] = 'postgresql' + default['postgresql']['uid'] = '26' + default['postgresql']['gid'] = '26' if node['platform_version'].to_f >= 6.0 && node['postgresql']['version'].to_f == 8.4 - default['postgresql']['client']['packages'] = ['postgresql-devel'] + default['postgresql']['client']['packages'] = 'postgresql-devel' default['postgresql']['server']['packages'] = ['postgresql-server'] default['postgresql']['contrib']['packages'] = ['postgresql-contrib'] end -when "opensuse" + if node['platform_version'].to_f >= 7.0 + default['postgresql']['version'] = '9.2' + default['postgresql']['client']['packages'] = 'postgresql-devel' + default['postgresql']['server']['packages'] = ['postgresql-server'] + default['postgresql']['contrib']['packages'] = ['postgresql-contrib'] + end - default['postgresql']['dir'] = "/var/lib/pgsql/data" +when 'opensuse', 'opensuseleap' - if node['platform_version'].to_f == 13.2 - default['postgresql']['version'] = '9.3' - default['postgresql']['client']['packages'] = ['postgresql93', 'postgresql93-devel'] - default['postgresql']['server']['packages'] = ['postgresql93-server'] - default['postgresql']['contrib']['packages'] = ['postgresql93-contrib'] - elsif node['platform_version'].to_f == 13.1 + default['postgresql']['dir'] = '/var/lib/pgsql/data' + default['postgresql']['uid'] = '26' + default['postgresql']['gid'] = '26' + + case node['platform_version'].to_f + when 13.1 default['postgresql']['version'] = '9.2' default['postgresql']['client']['packages'] = ['postgresql92', 'postgresql92-devel'] default['postgresql']['server']['packages'] = ['postgresql92-server'] default['postgresql']['contrib']['packages'] = ['postgresql92-contrib'] + when 13.2 + default['postgresql']['version'] = '9.3' + default['postgresql']['client']['packages'] = ['postgresql93', 'postgresql93-devel'] + default['postgresql']['server']['packages'] = ['postgresql93-server'] + default['postgresql']['contrib']['packages'] = ['postgresql93-contrib'] + else # opensuseleap + default['postgresql']['version'] = '9.4' + default['postgresql']['client']['packages'] = ['postgresql94', 'postgresql94-devel'] + default['postgresql']['server']['packages'] = ['postgresql94-server'] + default['postgresql']['contrib']['packages'] = ['postgresql94-contrib'] end - default['postgresql']['server']['service_name'] = "postgresql" + default['postgresql']['server']['service_name'] = 'postgresql' -when "suse" - if node['platform_version'].to_f <= 11.1 - default['postgresql']['version'] = "8.3" - default['postgresql']['client']['packages'] = ['postgresql', 'rubygem-pg'] - default['postgresql']['server']['packages'] = ['postgresql-server'] - default['postgresql']['contrib']['packages'] = ['postgresql-contrib'] - else - default['postgresql']['version'] = "9.1" - default['postgresql']['client']['packages'] = ['postgresql91', 'rubygem-pg'] - default['postgresql']['server']['packages'] = ['postgresql91-server'] - default['postgresql']['contrib']['packages'] = ['postgresql91-contrib'] - end - - default['postgresql']['dir'] = "/var/lib/pgsql/data" - default['postgresql']['server']['service_name'] = "postgresql" - -else - default['postgresql']['version'] = "8.4" - default['postgresql']['dir'] = "/etc/postgresql/8.4/main" - default['postgresql']['client']['packages'] = ["postgresql"] - default['postgresql']['server']['packages'] = ["postgresql"] - default['postgresql']['contrib']['packages'] = ["postgresql"] - default['postgresql']['server']['service_name'] = "postgresql" +when 'suse' # sles 12+ + default['postgresql']['version'] = '9.1' + default['postgresql']['client']['packages'] = ['postgresql91', 'rubygem-pg'] + default['postgresql']['server']['packages'] = ['postgresql91-server'] + default['postgresql']['contrib']['packages'] = ['postgresql91-contrib'] + default['postgresql']['dir'] = '/var/lib/pgsql/data' + default['postgresql']['server']['service_name'] = 'postgresql' end case node['platform_family'] @@ -264,13 +224,17 @@ when 'rhel', 'fedora', 'suse' end default['postgresql']['pg_hba'] = [ - {:type => 'local', :db => 'all', :user => 'postgres', :addr => nil, :method => 'ident'}, - {:type => 'local', :db => 'all', :user => 'all', :addr => nil, :method => 'ident'}, - {:type => 'host', :db => 'all', :user => 'all', :addr => '127.0.0.1/32', :method => 'md5'}, - {:type => 'host', :db => 'all', :user => 'all', :addr => '::1/128', :method => 'md5'} + { type: 'local', db: 'all', user: 'postgres', addr: nil, method: 'ident' }, + { type: 'local', db: 'all', user: 'all', addr: nil, method: 'ident' }, + { type: 'host', db: 'all', user: 'all', addr: '127.0.0.1/32', method: 'md5' }, + { type: 'host', db: 'all', user: 'all', addr: '::1/128', method: 'md5' }, ] -default['postgresql']['password'] = Hash.new +default['postgresql']['password'] = {} + +# set to install a specific version of the ruby gem pg +# if attribute is not defined, install will pick the latest available pg gem +default['postgresql']['pg_gem']['version'] = nil case node['platform_family'] when 'debian' @@ -278,4 +242,3 @@ when 'debian' end default['postgresql']['initdb_locale'] = 'UTF-8' - diff --git a/cookbooks/postgresql/attributes/yum_pgdg_packages.rb b/cookbooks/postgresql/attributes/yum_pgdg_packages.rb index 04f8155..0dfb8dc 100644 --- a/cookbooks/postgresql/attributes/yum_pgdg_packages.rb +++ b/cookbooks/postgresql/attributes/yum_pgdg_packages.rb @@ -1,452 +1,507 @@ +# frozen_string_literal: true # The PostgreSQL RPM Building Project built repository RPMs for easy # access to the PGDG yum repositories. Links to RPMs for installation # on the supported version/platform combinations are listed at # http://yum.postgresql.org/repopackages.php, and the links for -# PostgreSQL 9.2, 9.3 and 9.4 are captured below. +# PostgreSQL 9.2, 9.3, 9.4, 9.5 and 9.6 are captured below. # default['postgresql']['pgdg']['repo_rpm_url'] = { - "9.4" => { - "redhat" => { - "7" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/", - "package" => "pgdg-redhat94-9.4-1.noarch.rpm" - } - }, - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-i386/", - "package" => "pgdg-redhat94-9.4-1.noarch.rpm" + '9.6' => { + 'amazon' => { + '2015' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-i386/', + 'package' => 'pgdg-ami201503-96-9.6-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/", - "package" => "pgdg-redhat94-9.4-1.noarch.rpm" - } - }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-5-i386/", - "package" => "pgdg-redhat94-9.4-1.noarch.rpm" + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-ami201503-96-9.6-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-5-x86_64/", - "package" => "pgdg-redhat94-9.4-1.noarch.rpm" - } - } + }, }, - "centos" => { - "7" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/", - "package" => "pgdg-centos94-9.4-1.noarch.rpm" - } - }, - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-i386/", - "package" => "pgdg-centos94-9.4-1.noarch.rpm" + 'centos' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-centos96-9.6-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/", - "package" => "pgdg-centos94-9.4-1.noarch.rpm" - } }, - "5" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-5-x86_64/", - "package" => "pgdg-centos94-9.4-1.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-i386/', + 'package' => 'pgdg-centos96-9.6-3.noarch.rpm', }, - "i386" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-5-i386/", - "package" => "pgdg-centos94-9.4-1.noarch.rpm" - } - } + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-centos96-9.6-3.noarch.rpm', + }, + }, }, - "fedora" => { - "22" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/fedora/fedora-22-x86_64/", - "package" => "pgdg-fedora94-9.4-3.noarch.rpm" - } - }, - "21" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/fedora/fedora-21-x86_64/", - "package" => "pgdg-fedora94-9.4-2.noarch.rpm" - }, - "i386" => { - "url" => "http://yum.postgresql.org/9.4/fedora/fedora-21-i686/", - "package" => "pgdg-fedora94-9.4-2.noarch.rpm" - } - }, - "20" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/fedora/fedora-20-x86_64/", - "package" => "pgdg-fedora94-9.4-1.noarch.rpm" + 'redhat' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-redhat96-9.6-3.noarch.rpm', }, - "i386" => { - "url" => "http://yum.postgresql.org/9.4/fedora/fedora-20-i686/", - "package" => "pgdg-fedora94-9.4-1.noarch.rpm" - } - } + }, + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-i386/', + 'package' => 'pgdg-redhat96-9.6-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-redhat96-9.6-3.noarch.rpm', + }, + }, }, - "amazon" => { - "2015" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-i386/", - "package" => "pgdg-ami201503-94-9.4-1.noarch.rpm" + 'oracle' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-oraclelinux96-9.6-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/", - "package" => "pgdg-ami201503-94-9.4-1.noarch.rpm" - } - } + }, + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-i386/', + 'package' => 'pgdg-oraclelinux96-9.6-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-oraclelinux96-9.6-3.noarch.rpm', + }, + }, }, - "scientific" => { - "7" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/", - "package" => "pgdg-sl94-9.4-1.noarch.rpm" - } - }, - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-i386/", - "package" => "pgdg-sl94-9.4-1.noarch.rpm" + 'scientific' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-sl96-9.6-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/", - "package" => "pgdg-sl94-9.4-1.noarch.rpm" - } }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-5-i386/", - "package" => "pgdg-sl94-9.4-1.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-i386/', + 'package' => 'pgdg-sl96-9.6-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-5-x86_64/", - "package" => "pgdg-sl94-9.4-1.noarch.rpm" - } - } + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-sl96-9.6-3.noarch.rpm', + }, + }, }, - "oracle" => { - "7" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/", - "package" => "pgdg-oraclelinux94-9.4-1.noarch.rpm" - } - }, - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-i386/", - "package" => "pgdg-oraclelinux94-9.4-1.noarch.rpm" + 'fedora' => { + '22' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/fedora/fedora-22-x86_64/', + 'package' => 'pgdg-fedora96-9.6-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/", - "package" => "pgdg-oraclelinux94-9.4-1.noarch.rpm" - } - } - } + }, + '23' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/fedora/fedora-23-x86_64/', + 'package' => 'pgdg-fedora96-9.6-3.noarch.rpm', + }, + }, + '24' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/fedora/fedora-24-x86_64/', + 'package' => 'pgdg-fedora96-9.6-3.noarch.rpm', + }, + }, + '25' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.6/fedora/fedora-25-x86_64/', + 'package' => 'pgdg-fedora96-9.6-3.noarch.rpm', + }, + }, + }, }, - "9.3" => { - "amazon" => { - "2015" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" + '9.5' => { + 'amazon' => { + '2015' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-i386/', + 'package' => 'pgdg-ami201503-95-9.5-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-ami201503-95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - } }, - "2014" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - } - }, - "2013" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - } - } }, - "centos" => { - "7" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-7-x86_64/", - "package" => "pgdg-centos93-9.3-1.noarch.rpm" - } - }, - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/", - "package" => "pgdg-centos93-9.3-1.noarch.rpm" + 'centos' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-centos95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/", - "package" => "pgdg-centos93-9.3-1.noarch.rpm" - } }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-5-i386/", - "package" => "pgdg-centos93-9.3-1.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-i386/', + 'package' => 'pgdg-centos95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-5-x86_64/", - "package" => "pgdg-centos93-9.3-1.noarch.rpm" - } - } + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-centos95-9.5-3.noarch.rpm', + }, + }, }, - "redhat" => { - "7" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-7-x86_64/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - } - }, - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm", + 'redhat' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-redhat95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - } }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-5-i386/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-i386/', + 'package' => 'pgdg-redhat95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-5-x86_64/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - } - } + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-redhat95-9.5-3.noarch.rpm', + }, + }, }, - "oracle" => { - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" + 'oracle' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-oraclelinux95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - } }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-5-i386/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-i386/', + 'package' => 'pgdg-oraclelinux95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-5-x86_64/", - "package" => "pgdg-redhat93-9.3-1.noarch.rpm" - } - } + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-oraclelinux95-9.5-3.noarch.rpm', + }, + }, }, - "scientific" => { - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-i386/", - "package" => "pgdg-sl93-9.3-1.noarch.rpm" + 'scientific' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-sl95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/", - "package" => "pgdg-sl93-9.3-1.noarch.rpm" - } }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-5-i386/", - "package" => "pgdg-sl93-9.3-1.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-i386/', + 'package' => 'pgdg-sl95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/redhat/rhel-5-x86_64/", - "package" => "pgdg-sl93-9.3-1.noarch.rpm" - } - } + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-sl95-9.5-3.noarch.rpm', + }, + }, }, - "fedora" => { - "20" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/fedora/fedora-20-x86_64/", - "pakcage" => "pgdg-fedora93-9.3-1.noarch.rpm" - } - }, - "19" => { - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/fedora/fedora-19-x86_64/", - "pakcage" => "pgdg-fedora93-9.3-1.noarch.rpm" - } - }, - "18" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/fedora/fedora-18-i386/", - "package" => "pgdg-fedora93-9.3-1.noarch.rpm" + 'fedora' => { + '22' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/fedora/fedora-22-x86_64/', + 'package' => 'pgdg-fedora95-9.5-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/fedora/fedora-18-x86_64/", - "package" => "pgdg-fedora93-9.3-1.noarch.rpm" - } }, - "17" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.3/fedora/fedora-17-i386/", - "package" => "pgdg-fedora93-9.3-1.noarch.rpm" + '23' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/fedora/fedora-23-x86_64/', + 'package' => 'pgdg-fedora95-9.5-4.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.3/fedora/fedora-17-x86_64/", - "package" => "pgdg-fedora93-9.3-1.noarch.rpm" - } - } - } + }, + '24' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/fedora/fedora-24-x86_64/', + 'package' => 'pgdg-fedora95-9.5-4.noarch.rpm', + }, + }, + '25' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.5/fedora/fedora-25-x86_64/', + 'package' => 'pgdg-fedora95-9.5-4.noarch.rpm', + }, + }, + }, }, - "9.2" => { - "centos" => { - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-6-i386/", - "package" => "pgdg-centos92-9.2-7.noarch.rpm" + '9.4' => { + 'redhat' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-redhat94-9.4-2.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/", - "package" => "pgdg-centos92-9.2-7.noarch.rpm" - } }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-5-i386/", - "package" => "pgdg-centos92-9.2-7.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-i386/', + 'package' => 'pgdg-redhat94-9.4-2.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-5-x86_64/", - "package" => "pgdg-centos92-9.2-7.noarch.rpm" - } - } + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-redhat94-9.4-2.noarch.rpm', + }, + }, }, - "redhat" => { - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-6-i386/", - "package" => "pgdg-redhat92-9.2-7.noarch.rpm" + 'centos' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-centos94-9.4-2.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/", - "package" => "pgdg-redhat92-9.2-7.noarch.rpm" - } }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-5-i386/", - "package" => "pgdg-redhat92-9.2-7.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-i386/', + 'package' => 'pgdg-centos94-9.4-2.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-5-x86_64/", - "package" => "pgdg-redhat92-9.2-7.noarch.rpm" - } - } + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-centos94-9.4-2.noarch.rpm', + }, + }, + '5' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-5-i386/', + 'package' => 'pgdg-centos94-9.4-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-5-x86_64/', + 'package' => 'pgdg-centos94-9.4-3.noarch.rpm', + }, + }, }, - "oracle" => { - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-6-i386/", - "package" => "pgdg-redhat92-9.2-7.noarch.rpm" + 'fedora' => { + '22' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/fedora/fedora-22-x86_64/', + 'package' => 'pgdg-fedora94-9.4-4.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/", - "package" => "pgdg-redhat92-9.2-7.noarch.rpm" - } }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-5-i386/", - "package" => "pgdg-redhat92-9.2-7.noarch.rpm" + '23' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/fedora/fedora-23-x86_64/', + 'package' => 'pgdg-fedora94-9.4-5.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-5-x86_64/", - "package" => "pgdg-redhat92-9.2-7.noarch.rpm" - } - } + }, + '24' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/fedora/fedora-24-x86_64/', + 'package' => 'pgdg-fedora94-9.4-5.noarch.rpm', + }, + }, + '25' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/fedora/fedora-25-x86_64/', + 'package' => 'pgdg-fedora94-9.4-5.noarch.rpm', + }, + }, }, - "scientific" => { - "6" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-6-i386/", - "package" => "pgdg-sl92-9.2-8.noarch.rpm" + 'amazon' => { + '2015' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-i386/', + 'package' => 'pgdg-ami201503-94-9.4-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-ami201503-94-9.4-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/", - "package" => "pgdg-sl92-9.2-8.noarch.rpm" - } }, - "5" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-5-i386/", - "package" => "pgdg-sl92-9.2-8.noarch.rpm" - }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/redhat/rhel-5-x86_64/", - "package" => "pgdg-sl92-9.2-8.noarch.rpm" - } - } }, - "fedora" => { - "19" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/fedora/fedora-19-i386/", - "package" => "pgdg-fedora92-9.2-6.noarch.rpm" + 'scientific' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-sl94-9.4-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/fedora/fedora-19-x86_64/", - "package" => "pgdg-fedora92-9.2-6.noarch.rpm" - } }, - "18" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/fedora/fedora-18-i386/", - "package" => "pgdg-fedora92-9.2-6.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-i386/', + 'package' => 'pgdg-sl94-9.4-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-sl94-9.4-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/fedora/fedora-18-x86_64/", - "package" => "pgdg-fedora92-9.2-6.noarch.rpm" - } }, - "17" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/fedora/fedora-17-i386/", - "package" => "pgdg-fedora92-9.2-6.noarch.rpm" + }, + 'oracle' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-oraclelinux94-9.4-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/fedora/fedora-17-x86_64/", - "package" => "pgdg-fedora92-9.2-5.noarch.rpm" - } }, - "16" => { - "i386" => { - "url" => "http://yum.postgresql.org/9.2/fedora/fedora-16-i386/", - "package" => "pgdg-fedora92-9.2-5.noarch.rpm" + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-i386/', + 'package' => 'pgdg-oraclelinux94-9.4-3.noarch.rpm', }, - "x86_64" => { - "url" => "http://yum.postgresql.org/9.2/fedora/fedora-16-x86_64/", - "package" => "pgdg-fedora92-9.2-5.noarch.rpm" - } - } - } - } + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-oraclelinux94-9.4-3.noarch.rpm', + }, + }, + }, + }, + '9.3' => { + 'amazon' => { + '2015' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-i386/', + 'package' => 'pgdg-redhat93-9.3-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-redhat93-9.3-3.noarch.rpm', + }, + }, + '2014' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-i386/', + 'package' => 'pgdg-redhat93-9.3-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-redhat93-9.3-3.noarch.rpm', + }, + }, + }, + 'centos' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-centos93-9.3-3.noarch.rpm', + }, + }, + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-i386/', + 'package' => 'pgdg-centos93-9.3-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-centos93-9.3-3.noarch.rpm', + }, + }, + }, + 'fedora' => { + '23' => { + 'x86_64' => { + 'url' => 'https://yum.postgresql.org/9.3/fedora/fedora-23-x86_64/', + 'package' => 'pgdg-fedora93-9.3-4.noarch.rpm', + }, + }, + }, + 'redhat' => { + '7' => { + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-7-x86_64/', + 'package' => 'pgdg-redhat93-9.3-2.noarch.rpm', + }, + }, + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-i386/', + 'package' => 'pgdg-redhat93-9.3-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-redhat93-9.3-3.noarch.rpm', + }, + }, + }, + 'oracle' => { + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-i386/', + 'package' => 'pgdg-redhat93-9.3-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-redhat93-9.3-3.noarch.rpm', + }, + }, + }, + 'scientific' => { + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-i386/', + 'package' => 'pgdg-sl93-9.3-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-sl93-9.3-3.noarch.rpm', + }, + }, + '5' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-5-i386/', + 'package' => 'pgdg-sl93-9.3-3.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.3/redhat/rhel-5-x86_64/', + 'package' => 'pgdg-sl93-9.3-3.noarch.rpm', + }, + }, + }, + }, + '9.2' => { + 'centos' => { + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.2/redhat/rhel-6-i386/', + 'package' => 'pgdg-centos92-9.2-8.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-centos92-9.2-8.noarch.rpm', + }, + }, + }, + 'redhat' => { + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.2/redhat/rhel-6-i386/', + 'package' => 'pgdg-redhat92-9.2-9.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-redhat92-9.2-9.noarch.rpm', + }, + }, + }, + 'oracle' => { + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.2/redhat/rhel-6-i386/', + 'package' => 'pgdg-redhat92-9.2-9.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-redhat92-9.2-9.noarch.rpm', + }, + }, + }, + 'scientific' => { + '6' => { + 'i386' => { + 'url' => 'http://yum.postgresql.org/9.2/redhat/rhel-6-i386/', + 'package' => 'pgdg-sl92-9.2-10.noarch.rpm', + }, + 'x86_64' => { + 'url' => 'http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/', + 'package' => 'pgdg-sl92-9.2-10.noarch.rpm', + }, + }, + }, + }, } diff --git a/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb b/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb deleted file mode 100644 index 551bc6a..0000000 --- a/cookbooks/postgresql/files/default/tests/minitest/apt_pgdg_postgresql_test.rb +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright 2012, Opscode, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe 'postgresql::apt_pgdg_postgresql' do - include Helpers::Postgresql - - it 'removes the Pitti PPA sources.list' do - skip unless %w{debian}.include?(node['platform_family']) - file("/etc/apt/sources.list.d/pitti-postgresql-ppa").wont_exist - end - it 'creates the PGDG apt sources.list' do - skip unless %w{debian}.include?(node['platform_family']) - file("/etc/apt/sources.list.d/apt.postgresql.org.list").must_exist - end - - it 'installs postgresql-client-9.4' do - package("postgresql-client-9.4").must_be_installed - end - - it 'makes psql version 9.4 available' do - psql = shell_out("psql --version") - assert psql.stdout.include?("psql (PostgreSQL) 9.4") - end -end diff --git a/cookbooks/postgresql/files/default/tests/minitest/server_test.rb b/cookbooks/postgresql/files/default/tests/minitest/server_test.rb deleted file mode 100644 index ea50064..0000000 --- a/cookbooks/postgresql/files/default/tests/minitest/server_test.rb +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright 2012, Opscode, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require File.expand_path('../support/helpers', __FILE__) - -describe 'postgresql::server' do - include Helpers::Postgresql - - it 'installs the postgresql server packages' do - node['postgresql']['server']['packages'].each do |pkg| - package(pkg).must_be_installed - end - end - - it 'runs the postgresql service' do - service((node['postgresql']['server']['service_name'] || 'postgresql')).must_be_running - end - - it 'can connect to postgresql' do - Gem.clear_paths - require 'pg' - conn = PG::Connection.new( - :host => 'localhost', - :port => '5432', - :password => node['postgresql']['password']['postgres'], - :user => "postgres" - ) - assert_match(/localhost/, conn.host) - end - -end diff --git a/cookbooks/postgresql/libraries/default.rb b/cookbooks/postgresql/libraries/default.rb index 24b0e1b..61eaf39 100644 --- a/cookbooks/postgresql/libraries/default.rb +++ b/cookbooks/postgresql/libraries/default.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: false # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Library:: default # Author:: David Crane () # @@ -20,186 +21,179 @@ include Chef::Mixin::ShellOut module Opscode module PostgresqlHelpers - -####### -# Function to truncate value to 4 significant bits, render human readable. -# Used in recipes/config_initdb.rb to set this attribute: -# -# The memory settings (shared_buffers, effective_cache_size, work_mem, -# maintenance_work_mem and wal_buffers) will be rounded down to keep -# the 4 most significant bits, so that SHOW will be likely to use a -# larger divisor. The output is actually a human readable string that -# ends with "GB", "MB" or "kB" if over 1023, exactly what Postgresql -# will expect in a postgresql.conf setting. The output may be up to -# 6.25% less than the original value because of the rounding. -def binaryround(value) - - # Keep a multiplier which grows through powers of 1 - multiplier = 1 - - # Truncate value to 4 most significant bits - while value >= 16 - value = (value / 2).floor - multiplier = multiplier * 2 - end - - # Factor any remaining powers of 2 into the multiplier - while value == 2*((value / 2).floor) - value = (value / 2).floor - multiplier = multiplier * 2 - end - - # Factor enough powers of 2 back into the value to - # leave the multiplier as a power of 1024 that can - # be represented as units of "GB", "MB" or "kB". - if multiplier >= 1024*1024*1024 - while multiplier > 1024*1024*1024 - value = 2*value - multiplier = (multiplier/2).floor - end - multiplier = 1 - units = "GB" - - elsif multiplier >= 1024*1024 - while multiplier > 1024*1024 - value = 2*value - multiplier = (multiplier/2).floor - end - multiplier = 1 - units = "MB" - - elsif multiplier >= 1024 - while multiplier > 1024 - value = 2*value - multiplier = (multiplier/2).floor - end - multiplier = 1 - units = "kB" - - else - units = "" - end - - # Now we can return a nice human readable string. - return "#{multiplier * value}#{units}" -end - -####### -# Locale Configuration - -# Function to test the date order. -# Used in recipes/config_initdb.rb to set this attribute: -# node.default['postgresql']['config']['datestyle'] -def locale_date_order - # Test locale conversion of mon=11, day=22, year=33 - testtime = DateTime.new(2033,11,22,0,0,0,"-00:00") - #=> # - - # %x - Preferred representation for the date alone, no time - res = testtime.strftime("%x") - - if res.nil? - return 'mdy' - end - - posM = res.index("11") - posD = res.index("22") - posY = res.index("33") - - if (posM.nil? || posD.nil? || posY.nil?) - return 'mdy' - elseif (posY < posM && posM < posD) - return 'ymd' - elseif (posD < posM) - return 'dmy' - else - return 'mdy' - end -end - -####### -# Timezone Configuration -require 'find' - -# Function to determine where the system stored shared timezone data. -# Used in recipes/config_initdb.rb to detemine where it should have -# select_default_timezone(tzdir) search. -def pg_TZDIR() - # System time zone conversions are controlled by a timezone data file - # identified through environment variables (TZ and TZDIR) and/or file - # and directory naming conventions specific to the Linux distribution. - # Each of these timezone names will have been loaded into the PostgreSQL - # pg_timezone_names view by the package maintainer. + ####### + # Function to truncate value to 4 significant bits, render human readable. + # Used in recipes/config_initdb.rb to set this attribute: # - # Instead of using the timezone name configured as the system default, - # the PostgreSQL server uses ones named in postgresql.conf settings - # (timezone and log_timezone). The initdb utility does initialize those - # settings to the timezone name that corresponds to the system default. - # - # The system's timezone name is actually a filename relative to the - # shared zoneinfo directory. That is usually /usr/share/zoneinfo, but - # it was /usr/lib/zoneinfo in older distributions and can be anywhere - # if specified by the environment variable TZDIR. The tzset(3) manpage - # seems to indicate the following precedence: - tzdir = nil - if ::File.directory?("/usr/lib/zoneinfo") - tzdir = "/usr/lib/zoneinfo" - else - share_path = [ ENV['TZDIR'], "/usr/share/zoneinfo" ].compact.first - if ::File.directory?(share_path) - tzdir = share_path + # The memory settings (shared_buffers, effective_cache_size, work_mem, + # maintenance_work_mem and wal_buffers) will be rounded down to keep + # the 4 most significant bits, so that SHOW will be likely to use a + # larger divisor. The output is actually a human readable string that + # ends with "GB", "MB" or "kB" if over 1023, exactly what Postgresql + # will expect in a postgresql.conf setting. The output may be up to + # 6.25% less than the original value because of the rounding. + def binaryround(value) + # Keep a multiplier which grows through powers of 1 + multiplier = 1 + + # Truncate value to 4 most significant bits + while value >= 16 + value = (value / 2).floor + multiplier *= 2 + end + + # Factor any remaining powers of 2 into the multiplier + while value == 2 * (value / 2).floor + value = (value / 2).floor + multiplier *= 2 + end + + # Factor enough powers of 2 back into the value to + # leave the multiplier as a power of 1024 that can + # be represented as units of "GB", "MB" or "kB". + if multiplier >= 1024 * 1024 * 1024 + while multiplier > 1024 * 1024 * 1024 + value = 2 * value + multiplier = (multiplier / 2).floor end + multiplier = 1 + units = 'GB' + + elsif multiplier >= 1024 * 1024 + while multiplier > 1024 * 1024 + value = 2 * value + multiplier = (multiplier / 2).floor + end + multiplier = 1 + units = 'MB' + + elsif multiplier >= 1024 + while multiplier > 1024 + value = 2 * value + multiplier = (multiplier / 2).floor + end + multiplier = 1 + units = 'kB' + + else + units = '' + end + + # Now we can return a nice human readable string. + "#{multiplier * value}#{units}" end - return tzdir -end -####### -# Function to support select_default_timezone(tzdir), which is -# used in recipes/config_initdb.rb. -def validate_zone(tzname) - # PostgreSQL does not support leap seconds, so this function tests - # the usual Linux tzname convention to avoid a misconfiguration. - # Assume that the tzdata package maintainer has kept all timezone - # data files with support for leap seconds is kept under the - # so-named "right/" subdir of the shared zoneinfo directory. - # - # The original PostgreSQL initdb is not Unix-specific, so it did a - # very complicated, thorough test in its pg_tz_acceptable() function - # that I could not begin to understand how to do in ruby :). - # - # Testing the tzname is good enough, since a misconfiguration - # will result in an immediate fatal error when the PostgreSQL - # service is started, with pgstartup.log messages such as: - # LOG: time zone "right/US/Eastern" appears to use leap seconds - # DETAIL: PostgreSQL does not support leap seconds. + ####### + # Locale Configuration - if tzname.index("right/") == 0 - return false - else - return true + # Function to test the date order. + # Used in recipes/config_initdb.rb to set this attribute: + # node.default['postgresql']['config']['datestyle'] + def locale_date_order + # Test locale conversion of mon=11, day=22, year=33 + testtime = DateTime.new(2033, 11, 22, 0, 0, 0, '-00:00') + #=> # + + # %x - Preferred representation for the date alone, no time + res = testtime.strftime('%x') + + return 'mdy' if res.nil? + + posM = res.index('11') + posD = res.index('22') + posY = res.index('33') + + if posM.nil? || posD.nil? || posY.nil? + return 'mdy' + elseif (posY < posM && posM < posD) + return 'ymd' + elseif (posD < posM) + return 'dmy' + end + 'mdy' end -end -# Function to support select_default_timezone(tzdir), which is -# used in recipes/config_initdb.rb. -def scan_available_timezones(tzdir) - # There should be an /etc/localtime zoneinfo file that is a link to - # (or a copy of) a timezone data file under tzdir, which should have - # been installed under the "share" directory by the tzdata package. - # - # The initdb utility determines which shared timezone file is being - # used as the system's default /etc/localtime. The timezone name is - # the timezone file path relative to the tzdir. + ####### + # Timezone Configuration + require 'find' - bestzonename = nil + # Function to determine where the system stored shared timezone data. + # Used in recipes/config_initdb.rb to detemine where it should have + # select_default_timezone(tzdir) search. + def pg_TZDIR + # System time zone conversions are controlled by a timezone data file + # identified through environment variables (TZ and TZDIR) and/or file + # and directory naming conventions specific to the Linux distribution. + # Each of these timezone names will have been loaded into the PostgreSQL + # pg_timezone_names view by the package maintainer. + # + # Instead of using the timezone name configured as the system default, + # the PostgreSQL server uses ones named in postgresql.conf settings + # (timezone and log_timezone). The initdb utility does initialize those + # settings to the timezone name that corresponds to the system default. + # + # The system's timezone name is actually a filename relative to the + # shared zoneinfo directory. That is usually /usr/share/zoneinfo, but + # it was /usr/lib/zoneinfo in older distributions and can be anywhere + # if specified by the environment variable TZDIR. The tzset(3) manpage + # seems to indicate the following precedence: + tzdir = nil + if ::File.directory?('/usr/lib/zoneinfo') + tzdir = '/usr/lib/zoneinfo' + else + share_path = [ENV['TZDIR'], '/usr/share/zoneinfo'].compact.first + tzdir = share_path if ::File.directory?(share_path) + end + tzdir + end - if (tzdir.nil?) - Chef::Log.error("The zoneinfo directory not found (looked for /usr/share/zoneinfo and /usr/lib/zoneinfo)") - elsif !::File.exists?("/etc/localtime") - Chef::Log.error("The system zoneinfo file not found (looked for /etc/localtime)") - elsif ::File.directory?("/etc/localtime") - Chef::Log.error("The system zoneinfo file not found (/etc/localtime is a directory instead)") - elsif ::File.symlink?("/etc/localtime") + ####### + # Function to support select_default_timezone(tzdir), which is + # used in recipes/config_initdb.rb. + def validate_zone(tzname) + # PostgreSQL does not support leap seconds, so this function tests + # the usual Linux tzname convention to avoid a misconfiguration. + # Assume that the tzdata package maintainer has kept all timezone + # data files with support for leap seconds is kept under the + # so-named "right/" subdir of the shared zoneinfo directory. + # + # The original PostgreSQL initdb is not Unix-specific, so it did a + # very complicated, thorough test in its pg_tz_acceptable() function + # that I could not begin to understand how to do in ruby :). + # + # Testing the tzname is good enough, since a misconfiguration + # will result in an immediate fatal error when the PostgreSQL + # service is started, with pgstartup.log messages such as: + # LOG: time zone "right/US/Eastern" appears to use leap seconds + # DETAIL: PostgreSQL does not support leap seconds. + + if tzname.index('right/') == 0 + false + else + true + end + end + + # Function to support select_default_timezone(tzdir), which is + # used in recipes/config_initdb.rb. + def scan_available_timezones(tzdir) + # There should be an /etc/localtime zoneinfo file that is a link to + # (or a copy of) a timezone data file under tzdir, which should have + # been installed under the "share" directory by the tzdata package. + # + # The initdb utility determines which shared timezone file is being + # used as the system's default /etc/localtime. The timezone name is + # the timezone file path relative to the tzdir. + + bestzonename = nil + + if tzdir.nil? + Chef::Log.error('The zoneinfo directory not found (looked for /usr/share/zoneinfo and /usr/lib/zoneinfo)') + elsif !::File.exist?('/etc/localtime') + Chef::Log.error('The system zoneinfo file not found (looked for /etc/localtime)') + elsif ::File.directory?('/etc/localtime') + Chef::Log.error('The system zoneinfo file not found (/etc/localtime is a directory instead)') + elsif ::File.symlink?('/etc/localtime') # PostgreSQL initdb doesn't use the symlink target, but this # certainly will make sense to any system administrator. A full # scan of the tzdir to find the shortest filename could result @@ -207,147 +201,107 @@ def scan_available_timezones(tzdir) # in spite of what the sysadmin had specified in the symlink. # (There are many duplicates under tzdir, with the same timezone # content appearing as an average of 2-3 different file names.) - path = ::File.readlink("/etc/localtime") - bestzonename = path.gsub("#{tzdir}/","") - else # /etc/localtime is a file, so scan for it under tzdir - localtime_content = File.read("/etc/localtime") + path = ::File.realdirpath('/etc/localtime') + bestzonename = path.gsub("#{tzdir}/", '') + else # /etc/localtime is a file, so scan for it under tzdir + localtime_content = File.read('/etc/localtime') Find.find(tzdir) do |path| - # Only consider files (skip directories or symlinks) - if !::File.directory?(path) && !::File.symlink?(path) - # Ignore any file named "posixrules" or "localtime" - if ::File.basename(path) != "posixrules" && ::File.basename(path) != "localtime" - # Do consider if content exactly matches /etc/localtime. - if localtime_content == File.read(path) - tzname = path.gsub("#{tzdir}/","") - if validate_zone(tzname) - if (bestzonename.nil? || - tzname.length < bestzonename.length || - (tzname.length == bestzonename.length && - (tzname <=> bestzonename) < 0) - ) - bestzonename = tzname - end - end - end - end - end - end + # Only consider files (skip directories or symlinks) + next unless !::File.directory?(path) && !::File.symlink?(path) + # Ignore any file named "posixrules" or "localtime" + next unless ::File.basename(path) != 'posixrules' && ::File.basename(path) != 'localtime' + # Do consider if content exactly matches /etc/localtime. + next unless localtime_content == File.read(path) + tzname = path.gsub("#{tzdir}/", '') + next unless validate_zone(tzname) + if bestzonename.nil? || + tzname.length < bestzonename.length || + (tzname.length == bestzonename.length && + (tzname <=> bestzonename) < 0) + + bestzonename = tzname + end + end + end + + bestzonename end - return bestzonename -end + # Function to support select_default_timezone(tzdir), which is + # used in recipes/config_initdb.rb. + def identify_system_timezone(tzdir) + resultbuf = scan_available_timezones(tzdir) -# Function to support select_default_timezone(tzdir), which is -# used in recipes/config_initdb.rb. -def identify_system_timezone(tzdir) - resultbuf = scan_available_timezones(tzdir) - - if !resultbuf.nil? + if !resultbuf.nil? # Ignore Olson's rather silly "Factory" zone; use GMT instead - if (resultbuf <=> "Factory") == 0 - resultbuf = nil - end + resultbuf = nil if (resultbuf <=> 'Factory') == 0 - else + else # Did not find the timezone. Fallback to use a GMT zone. Note that the # Olson timezone database names the GMT-offset zones in POSIX style: plus # is west of Greenwich. testtime = DateTime.now - std_ofs = testtime.strftime("%:z").split(":")[0].to_i + std_ofs = testtime.strftime('%:z').split(':')[0].to_i resultbuf = [ - "Etc/GMT", - (-std_ofs > 0) ? "+" : "", - (-std_ofs).to_s - ].join('') + 'Etc/GMT', + -std_ofs > 0 ? '+' : '', + (-std_ofs).to_s, + ].join('') + end + + resultbuf end - return resultbuf -end + ####### + # Function to determine the name of the system's default timezone. + # Used in recipes/config_initdb.rb to set these attributes: + # node.default['postgresql']['config']['log_timezone'] + # node.default['postgresql']['config']['timezone'] + def select_default_timezone(tzdir) + system_timezone = nil -####### -# Function to determine the name of the system's default timezone. -# Used in recipes/config_initdb.rb to set these attributes: -# node.default['postgresql']['config']['log_timezone'] -# node.default['postgresql']['config']['timezone'] -def select_default_timezone(tzdir) - - system_timezone = nil - - # Check TZ environment variable - tzname = ENV['TZ'] - if !tzname.nil? && !tzname.empty? && validate_zone(tzname) + # Check TZ environment variable + tzname = ENV['TZ'] + if !tzname.nil? && !tzname.empty? && validate_zone(tzname) system_timezone = tzname - else + else # Nope, so try to identify system timezone from /etc/localtime tzname = identify_system_timezone(tzdir) - if validate_zone(tzname) - system_timezone = tzname - end + system_timezone = tzname if validate_zone(tzname) + end + + system_timezone end - return system_timezone -end - -####### -# Function to determine the name of the system's default timezone. -def get_result_orig(query) - # query could be a String or an Array of String - if (query.is_a?(String)) - stdin = query - else - stdin = query.join("\n") - end - @get_result ||= begin - cmd = shell_out("cat", :input => stdin) - cmd.stdout - end -end - -####### -# Function to execute an SQL statement in the default database. -# Input: Query could be a single String or an Array of String. -# Output: A String with |-separated columns and \n-separated rows. -# Note an empty output could mean psql couldn't connect. -# This is easiest for 1-field (1-row, 1-col) results, otherwise -# it will be complex to parse the results. -def execute_sql(query) - db_name = node['postgresql']['database_name'] - # query could be a String or an Array of String - statement = query.is_a?(String) ? query : query.join("\n") - @execute_sql ||= begin - cmd = shell_out("psql -q --tuples-only --no-align -d #{db_name} -f -", - :user => "postgres", - :input => statement - ) - # If psql fails, generally the postgresql service is down. - # Instead of aborting chef with a fatal error, let's just - # pass these non-zero exitstatus back as empty cmd.stdout. - if (cmd.exitstatus() == 0 and !cmd.stderr.empty?) - # An SQL failure is still a zero exitstatus, but then the - # stderr explains the error, so let's rais that as fatal. - Chef::Log.fatal("psql failed executing this SQL statement:\n#{statement}") - Chef::Log.fatal(cmd.stderr) - raise "SQL ERROR" + ####### + # Function to execute an SQL statement in the default database. + # Input: Query could be a single String or an Array of String. + # Output: A String with |-separated columns and \n-separated rows. + # Note an empty output could mean psql couldn't connect. + # This is easiest for 1-field (1-row, 1-col) results, otherwise + # it will be complex to parse the results. + def execute_sql(query, db_name = node['postgresql']['database_name']) + # query could be a String or an Array of String + statement = query.is_a?(String) ? query : query.join("\n") + cmd = shell_out("psql -q --tuples-only --no-align -d #{db_name} -f -", + user: 'postgres', + input: statement) + # If psql fails, generally the postgresql service is down. + # Instead of aborting chef with a fatal error, let's just + # pass these non-zero exitstatus back as empty cmd.stdout. + if cmd.exitstatus == 0 && !cmd.stderr.empty? + # An SQL failure is still a zero exitstatus, but then the + # stderr explains the error, so let's rais that as fatal. + Chef::Log.fatal("psql failed executing this SQL statement:\n#{statement}") + Chef::Log.fatal(cmd.stderr) + raise 'SQL ERROR' + end + cmd.stdout.chomp end - cmd.stdout.chomp - end -end -####### -# Function to determine if a standard contrib extension is already installed. -# Input: Extension name -# Output: true or false -# Best use as a not_if gate on bash "install-#{pg_ext}-extension" resource. -def extension_installed?(pg_ext) - @extension_installed ||= begin - installed=execute_sql("select 'installed' from pg_extension where extname = '#{pg_ext}';") - installed =~ /^installed$/ - end -end - -# End the Opscode::PostgresqlHelpers module + # End the Opscode::PostgresqlHelpers module end end diff --git a/cookbooks/postgresql/metadata.json b/cookbooks/postgresql/metadata.json index 0af3c80..419148d 100644 --- a/cookbooks/postgresql/metadata.json +++ b/cookbooks/postgresql/metadata.json @@ -1,57 +1 @@ -{ - "name": "postgresql", - "description": "Installs and configures postgresql for clients or servers", - "long_description": "Description\n===========\n\nInstalls and configures PostgreSQL as a client or a server.\n\nRequirements\n============\n\n## Platforms\n\n* Debian, Ubuntu\n* Red Hat/CentOS/Scientific (6.0+ required) - \"EL6-family\"\n* Fedora\n* SUSE\n\nTested on:\n\n* Ubuntu 12.04, 14.04, 14.10\n* Red Hat 6.1, Scientific 6.1, CentOS 6.3, 7.0, OpenSuse\n\n## Cookbooks\n\nRequires Opscode's `openssl` cookbook for secure password generation.\n\nRequires a C compiler and development headers in order to build the\n`pg` RubyGem to provide Ruby bindings in the `ruby` recipe.\n\nOpscode's `build-essential` cookbook provides this functionality on\nDebian, Ubuntu, and EL6-family.\n\nWhile not required, Opscode's `database` cookbook contains resources\nand providers that can interact with a PostgreSQL database. This\ncookbook is a dependency of database.\n\nAttributes\n==========\n\nThe following attributes are set based on the platform, see the\n`attributes/default.rb` file for default values.\n\n* `node['postgresql']['version']` - version of postgresql to manage\n* `node['postgresql']['dir']` - home directory of where postgresql\n data and configuration lives.\n\n* `node['postgresql']['client']['packages']` - An array of package names\n that should be installed on \"client\" systems.\n* `node['postgresql']['server']['packages']` - An array of package names\n that should be installed on \"server\" systems.\n* `node['postgresql']['server']['config_change_notify']` - Type of\n notification triggered when a config file changes.\n* `node['postgresql']['contrib']['packages']` - An array of package names\n that could be installed on \"server\" systems for useful sysadmin tools.\n\n* `node['postgresql']['enable_pgdg_apt']` - Whether to enable the apt repo\n by the PostgreSQL Global Development Group, which contains newer versions\n of PostgreSQL.\n\n* `node['postgresql']['enable_pgdg_yum']` - Whether to enable the yum repo\n by the PostgreSQL Global Development Group, which contains newer versions\n of PostgreSQL.\n\n* `node['postgresql']['initdb_locale']` - Sets the default locale for the\n database cluster. If this attribute is not specified, the locale is\n inherited from the environment that initdb runs in. Sometimes you must\n have a system locale that is not what you want for your database cluster,\n and this attribute addresses that scenario. Valid only for EL-family\n distros (RedHat/Centos/etc.).\n\nThe following attributes are generated in\n`recipe[postgresql::server]`.\n\nConfiguration\n-------------\n\nThe `postgresql.conf` and `pg_hba.conf` files are dynamically\ngenerated from attributes. Each key in `node['postgresql']['config']`\nis a postgresql configuration directive, and will be rendered in the\nconfig file. For example, the attribute:\n\n node['postgresql']['config']['listen_addresses'] = 'localhost'\n\nWill result in the following line in the `postgresql.conf` file:\n\n listen_addresses = 'localhost'\n\nThe attributes file contains default values for Debian and RHEL\nplatform families (per the `node['platform_family']`). These defaults\nhave disparity between the platforms because they were originally\nextracted from the postgresql.conf files in the previous version of\nthis cookbook, which differed in their default config. The resulting\nconfiguration files will be the same as before, but the content will\nbe dynamically rendered from the attributes. The helpful commentary\nwill no longer be present. You should consult the PostgreSQL\ndocumentation for specific configuration details.\n\nSee __Recipes__ `config_initdb` and `config_pgtune` below to\nauto-generate many postgresql.conf settings.\n\nFor values that are \"on\" or \"off\", they should be specified as literal\n`true` or `false`. String values will be used with single quotes. Any\nconfiguration option set to the literal `nil` will be skipped\nentirely. All other values (e.g., numeric literals) will be used as\nis. So for example:\n\n node.default['postgresql']['config']['logging_collector'] = true\n node.default['postgresql']['config']['datestyle'] = 'iso, mdy'\n node.default['postgresql']['config']['ident_file'] = nil\n node.default['postgresql']['config']['port'] = 5432\n\nWill result in the following config lines:\n\n logging_collector = 'on'\n datestyle = 'iso,mdy'\n port = 5432\n\n(no line printed for `ident_file` as it is `nil`)\n\nNote that the `unix_socket_directory` configuration was renamed to\n`unix_socket_directories` in Postgres 9.3 so make sure to use the\n`node['postgresql']['unix_socket_directories']` attribute instead of\n`node['postgresql']['unix_socket_directory']`.\n\nThe `pg_hba.conf` file is dynamically generated from the\n`node['postgresql']['pg_hba']` attribute. This attribute must be an\narray of hashes, each hash containing the authorization data. As it is\nan array, you can append to it in your own recipes. The hash keys in\nthe array must be symbols. Each hash will be written as a line in\n`pg_hba.conf`. For example, this entry from\n`node['postgresql']['pg_hba']`:\n\n [{:comment => '# Optional comment',\n :type => 'local', :db => 'all', :user => 'postgres', :addr => nil, :method => 'md5'}]\n\nWill result in the following line in `pg_hba.conf`:\n\n # Optional comment\n local all postgres md5\n\nUse `nil` if the CIDR-ADDRESS should be empty (as above).\nDon't provide a comment if none is desired in the `pg_hba.conf` file.\n\nNote that the following authorization rule is supplied automatically by\nthe cookbook template. The cookbook needs this to execute SQL in the\nPostgreSQL server without supplying the clear-text password (which isn't\nknown by the cookbook). Therefore, your `node['postgresql']['pg_hba']`\nattributes don't need to specify this authorization rule:\n\n # \"local\" is for Unix domain socket connections only\n local all all ident\n\n(By the way, the template uses `peer` instead of `ident` for PostgreSQL-9.1\nand above, which has the same effect.)\n\nRecipes\n=======\n\ndefault\n-------\n\nIncludes the client recipe.\n\nclient\n------\n\nInstalls the packages defined in the\n`node['postgresql']['client']['packages']` attribute.\n\nruby\n----\n\nInstall the `pg` gem under Chef's Ruby environment so it can be used\nin other recipes. The build-essential packages and postgresql client\npackages will be installed during the compile phase, so that the\nnative extensions of `pg` can be compiled.\n\nserver\n------\n\nIncludes the `server_debian` or `server_redhat` recipe to get the\nappropriate server packages installed and service managed. Also\nmanages the configuration for the server:\n\n* generates a strong default password (via `openssl`) for `postgres`\n* sets the password for postgres\n* manages the `postgresql.conf` file.\n* manages the `pg_hba.conf` file.\n\nserver\\_debian\n--------------\n\nInstalls the postgresql server packages and sets up the service. You\nshould include the `postgresql::server` recipe, which will include\nthis on Debian platforms.\n\nserver\\_redhat\n--------------\n\nManages the postgres user and group (with UID/GID 26, per RHEL package\nconventions), installs the postgresql server packages, initializes the\ndatabase, and manages the postgresql service. You should include the\n`postgresql::server` recipe, which will include this on RHEL/Fedora\nplatforms.\n\nconfig\\_initdb\n--------------\n\nTakes locale and timezone settings from the system configuration.\nThis recipe creates `node.default['postgresql']['config']` attributes\nthat conform to the system's locale and timezone. In addition, this\nrecipe creates the same error reporting and logging settings that\n`initdb` provided: a rotation of 7 days of log files named\npostgresql-Mon.log, etc.\n\nThe default attributes created by this recipe are easy to override with\nnormal attributes because of Chef attribute precedence. For example,\nsuppose a DBA wanted to keep log files indefinitely, rolling over daily\nor when growing to 10MB. The Chef installation could include the\n`postgresql::config_initdb` recipe for the locale and timezone settings,\nbut customize the logging settings with these node JSON attributes:\n\n \"postgresql\": {\n \"config\": {\n \"log_rotation_age\": \"1d\",\n \"log_rotation_size\": \"10MB\",\n \"log_filename\": \"postgresql-%Y-%m-%d_%H%M%S.log\"\n }\n }\n\nCredits: This `postgresql::config_initdb` recipe is based on algorithms\nin the [source code](http://doxygen.postgresql.org/initdb_8c_source.html)\nfor the PostgreSQL `initdb` utility.\n\nconfig\\_pgtune\n--------------\n\nPerformance tuning.\nTakes the wimpy default postgresql.conf and expands the database server\nto be as powerful as the hardware it's being deployed on. This recipe\ncreates a baseline configuration of `node.default['postgresql']['config']`\nattributes in the right general range for a dedicated Postgresql system.\nMost installations won't need additional performance tuning.\n\nThe only decision you need to make is to choose a `db_type` from the\nfollowing database workloads. (See the recipe code comments for more\ndetailed descriptions.)\n\n * \"dw\" -- Data Warehouse\n * \"oltp\" -- Online Transaction Processing\n * \"web\" -- Web Application\n * \"mixed\" -- Mixed DW and OLTP characteristics\n * \"desktop\" -- Not a dedicated database\n\nThis recipe uses a performance model with three input parameters.\nThese node attributes are completely optional, but it is obviously\nimportant to choose the `db_type` correctly:\n\n * `node['postgresql']['config_pgtune']['db_type']` --\n Specifies database type from the list of five choices above.\n If not specified, the default is \"mixed\".\n\n * `node['postgresql']['config_pgtune']['max_connections']` --\n Specifies maximum number of connections expected.\n If not specified, it depends on database type:\n \"web\":200, \"oltp\":300, \"dw\":20, \"mixed\":80, \"desktop\":5\n\n * `node['postgresql']['config_pgtune']['total_memory']` --\n Specifies total system memory in kB. (E.g., \"49416564kB\".)\n If not specified, it will be taken from Ohai automatic attributes.\n This could be used to tune a system that isn't a dedicated database.\n\nThe default attributes created by this recipe are easy to override with\nnormal attributes because of Chef attribute precedence. For example, if\nyou are running application benchmarks to try different buffer cache\nsizes, you would experiment with this node JSON attribute:\n\n \"postgresql\": {\n \"config\": {\n \"shared_buffers\": \"3GB\"\n }\n }\n\nNote that the recipe uses `max_connections` in its computations. If\nyou want to override that setting, you should specify\n`node['postgresql']['config_pgtune']['max_connections']` instead of\n`node['postgresql']['config']['max_connections']`.\n\nCredits: This `postgresql::config_pgtune` recipe is based on the\n[pgtune python script](https://github.com/gregs1104/pgtune)\ndeveloped by\n[Greg Smith](http://notemagnet.blogspot.com/2008/11/automating-initial-postgresqlconf.html)\nand\n[other pgsql-hackers](http://www.postgresql.org/message-id/491C6CDC.8090506@agliodbs.com).\n\ncontrib\n-------\n\nInstalls the packages defined in the\n`node['postgresql']['contrib']['packages']` attribute. The contrib\ndirectory of the PostgreSQL distribution includes porting tools,\nanalysis utilities, and plug-in features that database engineers often\nrequire. Some (like `pgbench`) are executable. Others (like\n`pg_buffercache`) would need to be installed into the database.\n\nAlso installs any contrib module extensions defined in the\n`node['postgresql']['contrib']['extensions']` attribute. These will be\navailable in any subsequently created databases in the cluster, because\nthey will be installed into the `template1` database using the\n`CREATE EXTENSION` command. For example, it is often necessary/helpful\nfor problem troubleshooting and maintenance planning to install the\nviews and functions in these [standard instrumentation extensions]\n(http://www.postgresql.org/message-id/flat/4DC32600.6080900@pgexperts.com#4DD3D6C6.5060006@2ndquadrant.com):\n\n node['postgresql']['contrib']['extensions'] = [\n \"pageinspect\",\n \"pg_buffercache\",\n \"pg_freespacemap\",\n \"pgrowlocks\",\n \"pg_stat_statements\",\n \"pgstattuple\"\n ]\n\nNote that the `pg_stat_statements` view only works if `postgresql.conf`\nloads its shared library, which can be done with this node attribute:\n\n node['postgresql']['config']['shared_preload_libraries'] = 'pg_stat_statements'\n\nIf using `shared_preload_libraries` in combination with the `contrib` recipe,\nmake sure that the `contrib` recipe is called before the `server` recipe (to\nensure the dependencies are installed and setup in order).\n\napt\\_pgdg\\_postgresql\n----------------------\n\nEnables the PostgreSQL Global Development Group yum repository\nmaintained by Devrim Gündüz for updated PostgreSQL packages.\n(The PGDG is the groups that develops PostgreSQL.)\nAutomatically included if the `node['postgresql']['enable_pgdg_apt']`\nattribute is true. Also set the\n`node['postgresql']['client']['packages']` and\n`node['postgresql']['server]['packages']` to the list of packages to\nuse from this repository, and set the `node['postgresql']['version']`\nattribute to the version to use (e.g., \"9.2\").\n\nyum\\_pgdg\\_postgresql\n---------------------\n\nEnables the PostgreSQL Global Development Group yum repository\nmaintained by Devrim Gündüz for updated PostgreSQL packages.\n(The PGDG is the groups that develops PostgreSQL.)\nAutomatically included if the `node['postgresql']['enable_pgdg_yum']`\nattribute is true. Also use `override_attributes` to set a number of\nvalues that will need to have embedded version numbers. For example:\n\n node['postgresql']['enable_pgdg_yum'] = true\n node['postgresql']['version'] = \"9.2\"\n node['postgresql']['dir'] = \"/var/lib/pgsql/9.2/data\"\n node['postgresql']['config']['data_directory'] = node['postgresql']['dir']\n node['postgresql']['client']['packages'] = [\"postgresql92\", \"postgresql92-devel\"]\n node['postgresql']['server']['packages'] = [\"postgresql92-server\"]\n node['postgresql']['server']['service_name'] = \"postgresql-9.2\"\n node['postgresql']['contrib']['packages'] = [\"postgresql92-contrib\"]\n\nYou may set `node['postgresql']['pgdg']['repo_rpm_url']` attributes\nto pick up recent [PGDG repo packages](http://yum.postgresql.org/repopackages.php).\n\nResources/Providers\n===================\n\nSee the [database](http://community.opscode.com/cookbooks/database)\nfor resources and providers that can be used for managing PostgreSQL\nusers and databases.\n\nUsage\n=====\n\nOn systems that need to connect to a PostgreSQL database, add to a run\nlist `recipe[postgresql]` or `recipe[postgresql::client]`.\n\nOn systems that should be PostgreSQL servers, use\n`recipe[postgresql::server]` on a run list. This recipe does set a\npassword for the `postgres` user.\nIf you're using `chef server`, if the attribute\n`node['postgresql']['password']['postgres']` is not found,\nthe recipe generates a random password and performs a node.save.\n(TODO: This is broken, as it disables the password.)\nIf you're using `chef-solo`, you'll need\nto set the attribute `node['postgresql']['password']['postgres']` in\nyour node's `json_attribs` file or in a role.\n\nOn Debian family systems, SSL will be enabled, as the packages on\nDebian/Ubuntu also generate the SSL certificates. If you use another\nplatform and wish to use SSL in postgresql, then generate your SSL\ncertificates and distribute them in your own cookbook, and set the\n`node['postgresql']['config']['ssl']` attribute to true in your\nrole/cookboook/node.\n\nOn server systems, the postgres server is restarted when a configuration\nfile changes. This can be changed to reload only by setting the\nfollowing attribute:\n\n node['postgresql']['server']['config_change_notify'] = :reload\n\nChef Solo Note\n==============\n\nThe following node attribute is stored on the Chef Server when using\n`chef-client`. Because `chef-solo` does not connect to a server or\nsave the node object at all, to have the password persist across\n`chef-solo` runs, you must specify them in the `json_attribs` file\nused. For Example:\n\n {\n \"postgresql\": {\n \"password\": {\n \"postgres\": \"iloverandompasswordsbutthiswilldo\"\n }\n },\n \"run_list\": [\"recipe[postgresql::server]\"]\n }\n\nThat should actually be the \"encrypted password\" instead of cleartext,\nso you should generate it as an md5 hash using the PostgreSQL algorithm.\n\n* You could copy the md5-hashed password from an existing postgres\ndatabase if you have `postgres` access and want to use the same password:
\n`select * from pg_shadow where usename='postgres';`\n* You can run this from any postgres database session to use a new password:
\n`select 'md5'||md5('iloverandompasswordsbutthiswilldo'||'postgres');`\n* You can run this from a linux commandline:
\n`echo -n 'iloverandompasswordsbutthiswilldo''postgres' | openssl md5 | sed -e 's/.* /md5/'`\n\nLicense and Author\n==================\n\n- Author:: Joshua Timberman ()\n- Author:: Lamont Granquist ()\n- Author:: Chris Roberts ()\n- Author:: David Crane ()\n- Author:: Aaron Baer ()\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", - "maintainer": "Heavy Water Operations, LLC", - "maintainer_email": "helpdesk@heavywater.io", - "license": "Apache 2.0", - "platforms": { - "ubuntu": "< 14.10", - "debian": ">= 0.0.0", - "fedora": ">= 0.0.0", - "suse": ">= 0.0.0", - "opensuse": ">= 0.0.0", - "amazon": ">= 0.0.0", - "redhat": "~> 6.0", - "centos": "~> 6.0", - "scientific": "~> 6.0", - "oracle": "~> 6.0" - }, - "dependencies": { - "apt": ">= 1.9.0", - "build-essential": ">= 0.0.0", - "openssl": "~> 4.0" - }, - "recommendations": { - - }, - "suggestions": { - - }, - "conflicting": { - - }, - "providing": { - - }, - "replacing": { - - }, - "attributes": { - - }, - "groupings": { - - }, - "recipes": { - "postgresql": "Includes postgresql::client", - "postgresql::ruby": "Installs pg gem for Ruby bindings", - "postgresql::client": "Installs postgresql client package(s)", - "postgresql::server": "Installs postgresql server packages, templates", - "postgresql::server_redhat": "Installs postgresql server packages, redhat family style", - "postgresql::server_debian": "Installs postgresql server packages, debian family style" - }, - "version": "4.0.0", - "source_url": "", - "issues_url": "" -} +{"name":"postgresql","version":"6.1.1","description":"Installs and configures postgresql for clients or servers","long_description":"# postgresql cookbook\n\n[![Build Status](https://travis-ci.org/sous-chefs/postgresql.svg?branch=master)](https://travis-ci.org/sous-chefs/postgresql) [![Cookbook Version](https://img.shields.io/cookbook/v/postgresql.svg)](https://supermarket.chef.io/cookbooks/postgresql)\n\nInstalls and configures PostgreSQL as a client or a server.\n\n## Requirements\n\n### Platforms\n\n- Debian 7+\n- Ubuntu 12.04+\n- Red Hat/CentOS/Scientific (6.0+ required) - \"EL6-family\"\n- Fedora\n- SLES 12+\n- openSUSE 13+ / openSUSE Leap\n\n### Chef\n\n- Chef 12.1+\n\n### Cookbooks\n\n- `compat_resource`\n- `openssl`\n- `build-essential`\n\n## Attributes\n\nThe following attributes are set based on the platform, see the `attributes/default.rb` file for default values.\n\n- `node['postgresql']['version']` - version of postgresql to manage\n- `node['postgresql']['dir']` - home directory of where postgresql data and configuration lives.\n- `node['postgresql']['client']['packages']` - An array of package names that should be installed on \"client\" systems.\n- `node['postgresql']['server']['packages']` - An array of package names that should be installed on \"server\" systems.\n- `node['postgresql']['server']['config_change_notify']` - Type of notification triggered when a config file changes.\n- `node['postgresql']['contrib']['packages']` - An array of package names that could be installed on \"server\" systems for useful sysadmin tools.\n- `node['postgresql']['enable_pgdg_apt']` - Whether to enable the apt repo by the PostgreSQL Global Development Group, which contains newer versions of PostgreSQL.\n- `node['postgresql']['enable_pgdg_yum']` - Whether to enable the yum repo by the PostgreSQL Global Development Group, which contains newer versions of PostgreSQL.\n- `node['postgresql']['initdb_locale']` - Sets the default locale for the database cluster. If this attribute is not specified, the locale is inherited from the environment that initdb runs in. Sometimes you must have a system locale that is not what you want for your database cluster, and this attribute addresses that scenario. Valid only for EL-family distros (RedHat/Centos/etc.).\n\nThe following attributes are generated in `recipe[postgresql::server]`.\n\n## Configuration\n\nThe `postgresql.conf` and `pg_hba.conf` files are dynamically generated from attributes. Each key in `node['postgresql']['config']` is a postgresql configuration directive, and will be rendered in the config file. For example, the attribute:\n\n```ruby\nnode['postgresql']['config']['listen_addresses'] = 'localhost'\n```\n\nWill result in the following line in the `postgresql.conf` file:\n\n```ruby\nlisten_addresses = 'localhost'\n```\n\nThe attributes file contains default values for Debian and RHEL platform families (per the `node['platform_family']`). These defaults have disparity between the platforms because they were originally extracted from the postgresql.conf files in the previous version of this cookbook, which differed in their default config. The resulting configuration files will be the same as before, but the content will be dynamically rendered from the attributes. The helpful commentary will no longer be present. You should consult the PostgreSQL documentation for specific configuration details.\n\nSee **Recipes** `config_initdb` and `config_pgtune` below to auto-generate many postgresql.conf settings.\n\nFor values that are \"on\" or \"off\", they should be specified as literal `true` or `false`. String values will be used with single quotes. Any configuration option set to the literal `nil` will be skipped entirely. All other values (e.g., numeric literals) will be used as is. So for example:\n\n```ruby\nnode.default['postgresql']['config']['logging_collector'] = true\nnode.default['postgresql']['config']['datestyle'] = 'iso, mdy'\nnode.default['postgresql']['config']['ident_file'] = nil\nnode.default['postgresql']['config']['port'] = 5432\n```\n\nWill result in the following config lines:\n\n```ruby\nlogging_collector = 'on'\ndatestyle = 'iso,mdy'\nport = 5432\n```\n\n(no line printed for `ident_file` as it is `nil`)\n\nNote that the `unix_socket_directory` configuration was renamed to `unix_socket_directories` in Postgres 9.3 so make sure to use the `node['postgresql']['unix_socket_directories']` attribute instead of `node['postgresql']['unix_socket_directory']`.\n\nThe `pg_hba.conf` file is dynamically generated from the `node['postgresql']['pg_hba']` attribute. This attribute must be an array of hashes, each hash containing the authorization data. As it is an array, you can append to it in your own recipes. The hash keys in the array must be symbols. Each hash will be written as a line in `pg_hba.conf`. For example, this entry from `node['postgresql']['pg_hba']`:\n\n```\n[{:comment => '# Optional comment',\n:type => 'local', :db => 'all', :user => 'postgres', :addr => nil, :method => 'md5'}]\n```\n\nWill result in the following line in `pg_hba.conf`:\n\n```\n# Optional comment\nlocal all postgres md5\n```\n\nUse `nil` if the CIDR-ADDRESS should be empty (as above). Don't provide a comment if none is desired in the `pg_hba.conf` file.\n\nNote that the following authorization rule is supplied automatically by the cookbook template. The cookbook needs this to execute SQL in the PostgreSQL server without supplying the clear-text password (which isn't known by the cookbook). Therefore, your `node['postgresql']['pg_hba']` attributes don't need to specify this authorization rule:\n\n```\n# \"local\" is for Unix domain socket connections only\nlocal all all ident\n```\n\n(By the way, the template uses `peer` instead of `ident` for PostgreSQL-9.1 and above, which has the same effect.)\n\n## Recipes\n\n### default\n\nIncludes the client recipe.\n\n### client\n\nInstalls the packages defined in the `node['postgresql']['client']['packages']` attribute.\n\n### ruby\n\nInstall the `pg` gem under Chef's Ruby environment so it can be used in other recipes. The build-essential packages and postgresql client packages will be installed during the compile phase, so that the native extensions of `pg` can be compiled.\n\n### server\n\nIncludes the `server_debian` or `server_redhat` recipe to get the appropriate server packages installed and service managed. Also manages the configuration for the server:\n\n- generates a strong default password (via `openssl`) for `postgres`\n- sets the password for postgres\n- manages the `postgresql.conf` file.\n- manages the `pg_hba.conf` file.\n\n### config_initdb\n\nTakes locale and timezone settings from the system configuration. This recipe creates `node.default['postgresql']['config']` attributes that conform to the system's locale and timezone. In addition, this recipe creates the same error reporting and logging settings that `initdb` provided: a rotation of 7 days of log files named postgresql-Mon.log, etc.\n\nThe default attributes created by this recipe are easy to override with normal attributes because of Chef attribute precedence. For example, suppose a DBA wanted to keep log files indefinitely, rolling over daily or when growing to 10MB. The Chef installation could include the `postgresql::config_initdb` recipe for the locale and timezone settings, but customize the logging settings with these node JSON attributes:\n\n```javascript\n\"postgresql\": {\n \"config\": {\n \"log_rotation_age\": \"1d\",\n \"log_rotation_size\": \"10MB\",\n \"log_filename\": \"postgresql-%Y-%m-%d_%H%M%S.log\"\n }\n}\n```\n\nCredits: This `postgresql::config_initdb` recipe is based on algorithms in the [source code](http://doxygen.postgresql.org/initdb_8c_source.html) for the PostgreSQL `initdb` utility.\n\n### config_pgtune\n\nPerformance tuning. Takes the wimpy default postgresql.conf and expands the database server to be as powerful as the hardware it's being deployed on. This recipe creates a baseline configuration of `node.default['postgresql']['config']` attributes in the right general range for a dedicated Postgresql system. Most installations won't need additional performance tuning.\n\nThe only decision you need to make is to choose a `db_type` from the following database workloads. (See the recipe code comments for more detailed descriptions.)\n\n- \"dw\" -- Data Warehouse\n- \"oltp\" -- Online Transaction Processing\n- \"web\" -- Web Application\n- \"mixed\" -- Mixed DW and OLTP characteristics\n- \"desktop\" -- Not a dedicated database\n\nThis recipe uses a performance model with three input parameters. These node attributes are completely optional, but it is obviously important to choose the `db_type` correctly:\n\n- `node['postgresql']['config_pgtune']['db_type']` -- Specifies database type from the list of five choices above. If not specified, the default is \"mixed\".\n\n- `node['postgresql']['config_pgtune']['max_connections']` -- Specifies maximum number of connections expected. If not specified, it depends on database type: \"web\":200, \"oltp\":300, \"dw\":20, \"mixed\":80, \"desktop\":5\n\n- `node['postgresql']['config_pgtune']['total_memory']` -- Specifies total system memory in kB. (E.g., \"49416564kB\".) If not specified, it will be taken from Ohai automatic attributes. This could be used to tune a system that isn't a dedicated database.\n\nThe default attributes created by this recipe are easy to override with normal attributes because of Chef attribute precedence. For example, if you are running application benchmarks to try different buffer cache sizes, you would experiment with this node JSON attribute:\n\n```javascript\n\"postgresql\": {\n \"config\": {\n \"shared_buffers\": \"3GB\"\n }\n}\n```\n\nNote that the recipe uses `max_connections` in its computations. If you want to override that setting, you should specify `node['postgresql']['config_pgtune']['max_connections']` instead of `node['postgresql']['config']['max_connections']`.\n\nCredits: This `postgresql::config_pgtune` recipe is based on the [pgtune python script](https://github.com/gregs1104/pgtune) developed by [Greg Smith](http://notemagnet.blogspot.com/2008/11/automating-initial-postgresqlconf.html) and [other pgsql-hackers](http://www.postgresql.org/message-id/491C6CDC.8090506@agliodbs.com).\n\n### contrib\n\nInstalls the packages defined in the `node['postgresql']['contrib']['packages']` attribute. The contrib directory of the PostgreSQL distribution includes porting tools, analysis utilities, and plug-in features that database engineers often require. Some (like `pgbench`) are executable. Others (like `pg_buffercache`) would need to be installed into the database.\n\nAlso installs any contrib module extensions defined in the `node['postgresql']['contrib']['extensions']` attribute. These will be available in any subsequently created databases in the cluster, because they will be installed into the `template1` database using the `CREATE EXTENSION` command. For example, it is often necessary/helpful for problem troubleshooting and maintenance planning to install the views and functions in these [standard instrumentation extensions] ([http://www.postgresql.org/message-id/flat/4DC32600.6080900@pgexperts.com#4DD3D6C6.5060006@2ndquadrant.com](mailto:http://www.postgresql.org/message-id/flat/4DC32600.6080900@pgexperts.com#4DD3D6C6.5060006@2ndquadrant.com)):\n\n```ruby\nnode['postgresql']['contrib']['extensions'] = [\n \"pageinspect\",\n \"pg_buffercache\",\n \"pg_freespacemap\",\n \"pgrowlocks\",\n \"pg_stat_statements\",\n \"pgstattuple\"\n]\n```\n\nNote that the `pg_stat_statements` view only works if `postgresql.conf` loads its shared library, which can be done with this node attribute:\n\n```ruby\nnode['postgresql']['config']['shared_preload_libraries'] = 'pg_stat_statements'\n```\n\nIf using `shared_preload_libraries` in combination with the `contrib` recipe, make sure that the `contrib` recipe is called before the `server` recipe (to ensure the dependencies are installed and setup in order).\n\n### apt_pgdg_postgresql\n\nEnables the PostgreSQL Global Development Group yum repository maintained by Devrim Gündüz for updated PostgreSQL packages. (The PGDG is the groups that develops PostgreSQL.) Automatically included if the `node['postgresql']['enable_pgdg_apt']` attribute is true. Also set the `node['postgresql']['client']['packages']` and `node['postgresql']['server]['packages']` to the list of packages to use from this repository, and set the `node['postgresql']['version']` attribute to the version to use (e.g., \"9.2\").\n\n### yum_pgdg_postgresql\n\nEnables the PostgreSQL Global Development Group yum repository maintained by Devrim Gündüz for updated PostgreSQL packages. (The PGDG is the groups that develops PostgreSQL.) Automatically included if the `node['postgresql']['enable_pgdg_yum']` attribute is true. Also use `override_attributes` to set a number of values that will need to have embedded version numbers. For example:\n\n```ruby\nnode['postgresql']['enable_pgdg_yum'] = true\nnode['postgresql']['version'] = \"9.4\"\nnode['postgresql']['dir'] = \"/var/lib/pgsql/9.4/data\"\nnode['postgresql']['config']['data_directory'] = node['postgresql']['dir']\nnode['postgresql']['client']['packages'] = [\"postgresql94\", \"postgresql94-devel\"]\nnode['postgresql']['server']['packages'] = [\"postgresql94-server\"]\nnode['postgresql']['server']['service_name'] = \"postgresql-9.4\"\nnode['postgresql']['contrib']['packages'] = [\"postgresql94-contrib\"]\nnode['postgresql']['setup_script'] = \"postgresql94-setup\"\n```\n\nYou may set `node['postgresql']['pgdg']['repo_rpm_url']` attributes to pick up recent [PGDG repo packages](http://yum.postgresql.org/repopackages.php).\n\n## Usage\n\nOn systems that need to connect to a PostgreSQL database, add to a run list `recipe[postgresql]` or `recipe[postgresql::client]`.\n\nOn systems that should be PostgreSQL servers, use `recipe[postgresql::server]` on a run list. This recipe does set a password for the `postgres` user. If you're using `chef server`, if the attribute `node['postgresql']['password']['postgres']` is not found, the recipe generates a random password and performs a node.save. (TODO: This is broken, as it disables the password.) If you're using `chef-solo`, you'll need to set the attribute `node['postgresql']['password']['postgres']` in your node's `json_attribs` file or in a role.\n\nOn Debian family systems, SSL will be enabled, as the packages on Debian/Ubuntu also generate the SSL certificates. If you use another platform and wish to use SSL in postgresql, then generate your SSL certificates and distribute them in your own cookbook, and set the `node['postgresql']['config']['ssl']` attribute to true in your role/cookboook/node.\n\nOn server systems, the postgres server is restarted when a configuration file changes. This can be changed to reload only by setting the following attribute:\n\n```ruby\nnode['postgresql']['server']['config_change_notify'] = :reload\n```\n\n## Chef Solo Note\n\nThe following node attribute is stored on the Chef Server when using `chef-client`. Because `chef-solo` does not connect to a server or save the node object at all, to have the password persist across `chef-solo` runs, you must specify them in the `json_attribs` file used. For Example:\n\n```\n{\n \"postgresql\": {\n \"password\": {\n \"postgres\": \"iloverandompasswordsbutthiswilldo\"\n }\n },\n \"run_list\": [\"recipe[postgresql::server]\"]\n}\n```\n\nThat should actually be the \"encrypted password\" instead of cleartext, so you should generate it as an md5 hash using the PostgreSQL algorithm.\n\n- You could copy the md5-hashed password from an existing postgres database if you have `postgres` access and want to use the same password:
\n `select * from pg_shadow where usename='postgres';`\n- You can run this from any postgres database session to use a new password:
\n `select 'md5'||md5('iloverandompasswordsbutthiswilldo'||'postgres');`\n- You can run this from a linux commandline:
\n `echo -n 'iloverandompasswordsbutthiswilldo''postgres' | openssl md5 | sed -e 's/.* /md5/'`\n\n## License\n\nCopyright 2010-2016, Chef Software, Inc.\n\n```text\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","maintainer":"Sous Chefs","maintainer_email":"help@sous-chefs.org","license":"Apache 2.0","platforms":{"ubuntu":">= 12.04","debian":">= 7.0","opensuse":">= 13.0","suse":">= 12.0","fedora":">= 0.0.0","opensuseleap":">= 0.0.0","amazon":">= 0.0.0","redhat":">= 6.0","centos":">= 6.0","scientific":">= 6.0","oracle":">= 6.0"},"dependencies":{"compat_resource":">= 12.16.3","build-essential":">= 2.0.0","openssl":">= 4.0"},"recommendations":{},"suggestions":{},"conflicting":{},"providing":{},"replacing":{},"attributes":{},"groupings":{},"recipes":{"postgresql::default":"Includes postgresql::client","postgresql::ruby":"Installs pg gem for Ruby bindings","postgresql::client":"Installs postgresql client package(s)","postgresql::server":"Installs postgresql server packages, templates"}} \ No newline at end of file diff --git a/cookbooks/postgresql/metadata.rb b/cookbooks/postgresql/metadata.rb deleted file mode 100644 index 30a4ae0..0000000 --- a/cookbooks/postgresql/metadata.rb +++ /dev/null @@ -1,28 +0,0 @@ -name "postgresql" -maintainer "Heavy Water Operations, LLC" -maintainer_email "helpdesk@heavywater.io" -license "Apache 2.0" -description "Installs and configures postgresql for clients or servers" -long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "4.0.0" -recipe "postgresql", "Includes postgresql::client" -recipe "postgresql::ruby", "Installs pg gem for Ruby bindings" -recipe "postgresql::client", "Installs postgresql client package(s)" -recipe "postgresql::server", "Installs postgresql server packages, templates" -recipe "postgresql::server_redhat", "Installs postgresql server packages, redhat family style" -recipe "postgresql::server_debian", "Installs postgresql server packages, debian family style" - - -supports "ubuntu", "< 14.10" - -%w{debian fedora suse opensuse amazon}.each do |os| - supports os -end - -%w{redhat centos scientific oracle}.each do |el| - supports el, "~> 6.0" -end - -depends "apt", ">= 1.9.0" -depends "build-essential" -depends "openssl", "~> 4.0" diff --git a/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb b/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb index bab1afe..437cf37 100644 --- a/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb +++ b/cookbooks/postgresql/recipes/apt_pgdg_postgresql.rb @@ -1,14 +1,4 @@ -if not %w(jessie squeeze wheezy sid lucid precise saucy trusty utopic).include? node['postgresql']['pgdg']['release_apt_codename'] - raise "Not supported release by PGDG apt repository" -end - -include_recipe 'apt' - -file "remove deprecated Pitti PPA apt repository" do - action :delete - path "/etc/apt/sources.list.d/pitti-postgresql-ppa" -end - +# frozen_string_literal: true apt_repository 'apt.postgresql.org' do uri 'http://apt.postgresql.org/pub/repos/apt' distribution "#{node['postgresql']['pgdg']['release_apt_codename']}-pgdg" diff --git a/cookbooks/postgresql/recipes/ca_certificates.rb b/cookbooks/postgresql/recipes/ca_certificates.rb index f187281..67b14ef 100644 --- a/cookbooks/postgresql/recipes/ca_certificates.rb +++ b/cookbooks/postgresql/recipes/ca_certificates.rb @@ -1,6 +1,2 @@ -# some older linux distributions have expired certificate bundles -# for pgdg repositories. Upgrading this package before trying to -# install postgresql is necessary. -package "ca-certificates" do - action :upgrade -end +# frozen_string_literal: true +Chef::Log.warn('The postgresql::ca-certificates recipe has been deprecated and will be removed in the next major release of the cookbook') diff --git a/cookbooks/postgresql/recipes/client.rb b/cookbooks/postgresql/recipes/client.rb index 20d6774..2158e48 100644 --- a/cookbooks/postgresql/recipes/client.rb +++ b/cookbooks/postgresql/recipes/client.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: client # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,23 +16,19 @@ # limitations under the License. # -include_recipe "postgresql::ca_certificates" - case node['platform_family'] when 'debian' if node['postgresql']['version'].to_f > 9.3 - node.set['postgresql']['enable_pgdg_apt'] = true + node.normal['postgresql']['enable_pgdg_apt'] = true end if node['postgresql']['enable_pgdg_apt'] include_recipe 'postgresql::apt_pgdg_postgresql' end -when 'rhel' +when 'rhel', 'fedora' if node['postgresql']['enable_pgdg_yum'] include_recipe 'postgresql::yum_pgdg_postgresql' end end -node['postgresql']['client']['packages'].each do |pkg| - package pkg -end +package node['postgresql']['client']['packages'] diff --git a/cookbooks/postgresql/recipes/config_initdb.rb b/cookbooks/postgresql/recipes/config_initdb.rb index 92973ef..f0d3844 100644 --- a/cookbooks/postgresql/recipes/config_initdb.rb +++ b/cookbooks/postgresql/recipes/config_initdb.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: config_initdb # Author:: David Crane () # @@ -69,23 +70,23 @@ # Locale Configuration # See libraries/default.rb for the locale_date_order() method. -node.default['postgresql']['config']['datestyle'] = "iso, #{locale_date_order()}" +node.default['postgresql']['config']['datestyle'] = "iso, #{locale_date_order}" # According to the locale(1) manpage, the locale settings are determined # by environment variables according to the following precedence: # LC_ALL > (LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME) > LANG. node.default['postgresql']['config']['lc_messages'] = - [ ENV['LC_ALL'], ENV['LC_MESSAGES'], ENV['LANG'] ].compact.first + [ENV['LC_ALL'], ENV['LC_MESSAGES'], ENV['LANG']].compact.first node.default['postgresql']['config']['lc_monetary'] = - [ ENV['LC_ALL'], ENV['LC_MONETARY'], ENV['LANG'] ].compact.first + [ENV['LC_ALL'], ENV['LC_MONETARY'], ENV['LANG']].compact.first node.default['postgresql']['config']['lc_numeric'] = - [ ENV['LC_ALL'], ENV['LC_NUMERIC'], ENV['LANG'] ].compact.first + [ENV['LC_ALL'], ENV['LC_NUMERIC'], ENV['LANG']].compact.first node.default['postgresql']['config']['lc_time'] = - [ ENV['LC_ALL'], ENV['LC_TIME'], ENV['LANG'] ].compact.first + [ENV['LC_ALL'], ENV['LC_TIME'], ENV['LANG']].compact.first node.default['postgresql']['config']['default_text_search_config'] = case ENV['LANG'] @@ -119,8 +120,6 @@ node.default['postgresql']['config']['default_text_search_config'] = 'pg_catalog.swedish' when /tr_.*/ 'pg_catalog.turkish' - else - nil end ####### @@ -130,11 +129,11 @@ node.default['postgresql']['config']['default_text_search_config'] = # defaults for the postgresql.cof settings. If the timezone cannot be # identified, do as initdb would do: leave it unspecified so PostgreSQL # uses it's internal default of GMT. -tzdirpath = pg_TZDIR() # See libraries/default.rb +tzdirpath = pg_TZDIR # See libraries/default.rb default_timezone = select_default_timezone(tzdirpath) # See libraries/default.rb -if !default_timezone.nil? - node.default['postgresql']['config']['log_timezone'] = default_timezone - node.default['postgresql']['config']['timezone'] = default_timezone +unless default_timezone.nil? + node.default['postgresql']['config']['log_timezone'] = default_timezone + node.default['postgresql']['config']['timezone'] = default_timezone end ####### diff --git a/cookbooks/postgresql/recipes/config_pgtune.rb b/cookbooks/postgresql/recipes/config_pgtune.rb index 361bae7..f34fa67 100644 --- a/cookbooks/postgresql/recipes/config_pgtune.rb +++ b/cookbooks/postgresql/recipes/config_pgtune.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: config_pgtune # Author:: David Crane () # @@ -86,35 +87,35 @@ # Parse out db_type option, or use default. db_type = 'mixed' -if (node['postgresql'].attribute?('config_pgtune') && node['postgresql']['config_pgtune'].attribute?('db_type')) +if node['postgresql'].attribute?('config_pgtune') && node['postgresql']['config_pgtune'].attribute?('db_type') db_type = node['postgresql']['config_pgtune']['db_type'] - if (!(["dw","oltp","web","mixed","desktop"].include?(db_type))) + unless %w(dw oltp web mixed desktop).include?(db_type) Chef::Log.fatal([ - "Bad value (#{db_type})", - "for node['postgresql']['config_pgtune']['db_type'] attribute.", - "Valid values are one of dw, oltp, web, mixed, desktop." - ].join(' ')) + "Bad value (#{db_type})", + "for node['postgresql']['config_pgtune']['db_type'] attribute.", + 'Valid values are one of dw, oltp, web, mixed, desktop.', + ].join(' ')) raise end end # Parse out max_connections option, or use a value based on db_type. con = -{ "web" => 200, - "oltp" => 300, - "dw" => 20, - "mixed" => 80, - "desktop" => 5 -}.fetch(db_type) + { 'web' => 200, + 'oltp' => 300, + 'dw' => 20, + 'mixed' => 80, + 'desktop' => 5, + }.fetch(db_type) -if (node['postgresql'].attribute?('config_pgtune') && node['postgresql']['config_pgtune'].attribute?('max_connections')) +if node['postgresql'].attribute?('config_pgtune') && node['postgresql']['config_pgtune'].attribute?('max_connections') max_connections = node['postgresql']['config_pgtune']['max_connections'].to_i if max_connections <= 0 Chef::Log.fatal([ - "Bad value (#{max_connections})", - "for node['postgresql']['config_pgtune']['max_connections'] attribute.", - "Valid values are non-zero integers only." - ].join(' ')) + "Bad value (#{max_connections})", + "for node['postgresql']['config_pgtune']['max_connections'] attribute.", + 'Valid values are non-zero integers only.', + ].join(' ')) raise end con = max_connections @@ -125,19 +126,19 @@ total_memory = node['memory']['total'] # Override max_connections with a node attribute if DevOps desires. # For example, on a system *not* dedicated to Postgresql. -if (node['postgresql'].attribute?('config_pgtune') && node['postgresql']['config_pgtune'].attribute?('total_memory')) +if node['postgresql'].attribute?('config_pgtune') && node['postgresql']['config_pgtune'].attribute?('total_memory') total_memory = node['postgresql']['config_pgtune']['total_memory'] - if (total_memory.match(/\A[1-9]\d*kB\Z/) == nil) + if total_memory.match(/\A[1-9]\d*kB\Z/).nil? Chef::Application.fatal!([ - "Bad value (#{total_memory})", - "for node['postgresql']['config_pgtune']['total_memory'] attribute.", - "Valid values are non-zero integers followed by kB (e.g., 49416564kB)." - ].join(' ')) + "Bad value (#{total_memory})", + "for node['postgresql']['config_pgtune']['total_memory'] attribute.", + 'Valid values are non-zero integers followed by kB (e.g., 49416564kB).', + ].join(' ')) end end # Ohai reports node[:memory][:total] in kB, as in "921756kB" -mem = total_memory.split("kB")[0].to_i / 1024 # in MB +mem = total_memory.split('kB')[0].to_i / 1024 # in MB ####### # RAM-related settings computed as in Greg Smith's pgtune script. @@ -152,79 +153,73 @@ node.default['postgresql']['config']['max_connections'] = con # for low memory systems. In that case, the calculation is skipped, # leaving the built-in Postgresql settings, which are actually # intended for those low memory systems. -if (mem >= 256) +if mem >= 256 # (2) shared_buffers # Sets the number of shared memory buffers used by the server. shared_buffers = - { "web" => mem/4, - "oltp" => mem/4, - "dw" => mem/4, - "mixed" => mem/4, - "desktop" => mem/16 - }.fetch(db_type) + { 'web' => mem / 4, + 'oltp' => mem / 4, + 'dw' => mem / 4, + 'mixed' => mem / 4, + 'desktop' => mem / 16, + }.fetch(db_type) # Robert Haas has advised to cap the size of shared_buffers based on # the memory architecture: 2GB on 32-bit and 8GB on 64-bit machines. # http://rhaas.blogspot.com/2012/03/tuning-sharedbuffers-and-walbuffers.html case node['kernel']['machine'] - when "i386" # 32-bit machines - if shared_buffers > 2*1024 - shared_buffers = 2*1024 - end - when "x86_64" # 64-bit machines - if shared_buffers > 8*1024 - shared_buffers = 8*1024 - end + when 'i386' # 32-bit machines + shared_buffers = 2 * 1024 if shared_buffers > 2 * 1024 + when 'x86_64' # 64-bit machines + shared_buffers = 8 * 1024 if shared_buffers > 8 * 1024 end - node.default['postgresql']['config']['shared_buffers'] = binaryround(shared_buffers*1024*1024) + node.default['postgresql']['config']['shared_buffers'] = binaryround(shared_buffers * 1024 * 1024) # (3) effective_cache_size # Sets the planner's assumption about the size of the disk cache. # That is, the portion of the kernel's disk cache that will be # used for PostgreSQL data files. effective_cache_size = - { "web" => mem * 3 / 4, - "oltp" => mem * 3 / 4, - "dw" => mem * 3 / 4, - "mixed" => mem * 3 / 4, - "desktop" => mem / 4 - }.fetch(db_type) + { 'web' => mem * 3 / 4, + 'oltp' => mem * 3 / 4, + 'dw' => mem * 3 / 4, + 'mixed' => mem * 3 / 4, + 'desktop' => mem / 4, + }.fetch(db_type) - node.default['postgresql']['config']['effective_cache_size'] = binaryround(effective_cache_size*1024*1024) + node.default['postgresql']['config']['effective_cache_size'] = binaryround(effective_cache_size * 1024 * 1024) # (4) work_mem # Sets the maximum memory to be used for query workspaces. mem_con_v = (mem.to_f / con).ceil work_mem = - { "web" => mem_con_v, - "oltp" => mem_con_v, - "dw" => mem_con_v / 2, - "mixed" => mem_con_v / 2, - "desktop" => mem_con_v / 6 + { 'web' => mem_con_v, + 'oltp' => mem_con_v, + 'dw' => mem_con_v / 2, + 'mixed' => mem_con_v / 2, + 'desktop' => mem_con_v / 6, }.fetch(db_type) - node.default['postgresql']['config']['work_mem'] = binaryround(work_mem*1024*1024) + node.default['postgresql']['config']['work_mem'] = binaryround(work_mem * 1024 * 1024) # (5) maintenance_work_mem # Sets the maximum memory to be used for maintenance operations. # This includes operations such as VACUUM and CREATE INDEX. maintenance_work_mem = - { "web" => mem / 16, - "oltp" => mem / 16, - "dw" => mem / 8, - "mixed" => mem / 16, - "desktop" => mem / 16 - }.fetch(db_type) + { 'web' => mem / 16, + 'oltp' => mem / 16, + 'dw' => mem / 8, + 'mixed' => mem / 16, + 'desktop' => mem / 16, + }.fetch(db_type) # Cap maintenence RAM at 1GB on servers with lots of memory - if (maintenance_work_mem > 1*1024) - maintenance_work_mem = 1*1024 - end + maintenance_work_mem = 1 * 1024 if maintenance_work_mem > 1 * 1024 - node.default['postgresql']['config']['maintenance_work_mem'] = binaryround(maintenance_work_mem*1024*1024) + node.default['postgresql']['config']['maintenance_work_mem'] = binaryround(maintenance_work_mem * 1024 * 1024) end @@ -235,25 +230,29 @@ end # (6) checkpoint_segments # Sets the maximum distance in log segments between automatic WAL checkpoints. checkpoint_segments = -{ "web" => 8, - "oltp" => 16, - "dw" => 64, - "mixed" => 16, - "desktop" => 3 -}.fetch(db_type) + { 'web' => 8, + 'oltp' => 16, + 'dw' => 64, + 'mixed' => 16, + 'desktop' => 3, + }.fetch(db_type) -node.default['postgresql']['config']['checkpoint_segments'] = checkpoint_segments +if node['postgresql']['version'].to_f >= 9.5 + node.default['postgresql']['config']['max_wal_size'] = ((3 * checkpoint_segments) * 16).to_s + 'MB' +else + node.default['postgresql']['config']['checkpoint_segments'] = checkpoint_segments +end # (7) checkpoint_completion_target # Time spent flushing dirty buffers during checkpoint, as fraction # of checkpoint interval. checkpoint_completion_target = -{ "web" => "0.7", - "oltp" => "0.9", - "dw" => "0.9", - "mixed" => "0.9", - "desktop" => "0.5" -}.fetch(db_type) + { 'web' => '0.7', + 'oltp' => '0.9', + 'dw' => '0.9', + 'mixed' => '0.9', + 'desktop' => '0.5', + }.fetch(db_type) node.default['postgresql']['config']['checkpoint_completion_target'] = checkpoint_completion_target @@ -264,9 +263,9 @@ node.default['postgresql']['config']['checkpoint_completion_target'] = checkpoin if node['postgresql']['version'].to_f < 9.1 wal_buffers = 512 * checkpoint_segments # The pgtune seems to use 1kB units for wal_buffers - node.default['postgresql']['config']['wal_buffers'] = binaryround(wal_buffers*1024) + node.default['postgresql']['config']['wal_buffers'] = binaryround(wal_buffers * 1024) else - node.default['postgresql']['config']['wal_buffers'] = "-1" + node.default['postgresql']['config']['wal_buffers'] = '-1' end # (9) default_statistics_target @@ -274,11 +273,11 @@ end # that have not had a column-specific target set via # ALTER TABLE SET STATISTICS. default_statistics_target = -{ "web" => 100, - "oltp" => 100, - "dw" => 500, - "mixed" => 100, - "desktop" => 100 -}.fetch(db_type) + { 'web' => 100, + 'oltp' => 100, + 'dw' => 500, + 'mixed' => 100, + 'desktop' => 100, + }.fetch(db_type) node.default['postgresql']['config']['default_statistics_target'] = default_statistics_target diff --git a/cookbooks/postgresql/recipes/contrib.rb b/cookbooks/postgresql/recipes/contrib.rb index e4cae04..8990b58 100644 --- a/cookbooks/postgresql/recipes/contrib.rb +++ b/cookbooks/postgresql/recipes/contrib.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: contrib # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,26 +20,14 @@ db_name = node['postgresql']['database_name'] # Install the PostgreSQL contrib package(s) from the distribution, # as specified by the node attributes. -node['postgresql']['contrib']['packages'].each do |pg_pack| +package node['postgresql']['contrib']['packages'] - package pg_pack - -end - -include_recipe "postgresql::server" +include_recipe 'postgresql::server' # Install PostgreSQL contrib extentions into the database, as specified by the # node attribute node['postgresql']['database_name']. -if (node['postgresql']['contrib'].attribute?('extensions')) +if node['postgresql']['contrib'].attribute?('extensions') node['postgresql']['contrib']['extensions'].each do |pg_ext| - bash "install-#{pg_ext}-extension" do - user 'postgres' - code <<-EOH - echo 'CREATE EXTENSION IF NOT EXISTS "#{pg_ext}";' | psql -d "#{db_name}" - EOH - action :run - ::Chef::Resource.send(:include, Opscode::PostgresqlHelpers) - not_if {extension_installed?(pg_ext)} - end + postgresql_extension "#{db_name}/#{pg_ext}" end end diff --git a/cookbooks/postgresql/recipes/default.rb b/cookbooks/postgresql/recipes/default.rb index ea68f9b..0a25b15 100644 --- a/cookbooks/postgresql/recipes/default.rb +++ b/cookbooks/postgresql/recipes/default.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: default # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,4 +16,4 @@ # limitations under the License. # -include_recipe "postgresql::client" +include_recipe 'postgresql::client' diff --git a/cookbooks/postgresql/recipes/ruby.rb b/cookbooks/postgresql/recipes/ruby.rb index 68c9c71..2ab4bdc 100644 --- a/cookbooks/postgresql/recipes/ruby.rb +++ b/cookbooks/postgresql/recipes/ruby.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: false # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: ruby # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,70 +22,51 @@ begin require 'pg' rescue LoadError - - if platform_family?('ubuntu', 'debian') - e = execute 'apt-get update' do + if platform_family?('debian') + e = apt_update 'update' do action :nothing end - e.run_action(:run) unless ::File.exists?('/var/lib/apt/periodic/update-success-stamp') + e.run_action(:update) end - node.set['build-essential']['compile_time'] = true - include_recipe "build-essential" + node.override['build-essential']['compile_time'] = true + include_recipe 'build-essential' - if node['postgresql']['enable_pgdg_yum'] - package "ca-certificates" do - action :nothing - end.run_action(:upgrade) - - include_recipe "postgresql::yum_pgdg_postgresql" + if node['postgresql']['enable_pgdg_yum'] && platform_family?('rhel', 'fedora') + include_recipe 'postgresql::yum_pgdg_postgresql' rpm_platform = node['platform'] - rpm_platform_version = node['platform_version'].to_f.to_i.to_s + rpm_platform_version = node['platform_version'].to_i.to_s arch = node['kernel']['machine'] - resources("remote_file[#{Chef::Config[:file_cache_path]}/#{node[:postgresql][:pgdg][:repo_rpm_url][node[:postgresql][:version]][rpm_platform][rpm_platform_version][arch][:package]}]").run_action(:create) - resources("package[#{node[:postgresql][:pgdg][:repo_rpm_url][node[:postgresql][:version]][rpm_platform][rpm_platform_version][arch][:package]}]").run_action(:install) + resources("remote_file[#{Chef::Config[:file_cache_path]}/#{node['postgresql']['pgdg']['repo_rpm_url'][node['postgresql']['version']][rpm_platform][rpm_platform_version][arch]['package']}]").run_action(:create) + resources("package[#{node['postgresql']['pgdg']['repo_rpm_url'][node['postgresql']['version']][rpm_platform][rpm_platform_version][arch]['package']}]").run_action(:install) ENV['PATH'] = "/usr/pgsql-#{node['postgresql']['version']}/bin:#{ENV['PATH']}" - - node['postgresql']['client']['packages'].each do |pkg| - package pkg do - action :nothing - end.run_action(:install) - end - end - if node['postgresql']['enable_pgdg_apt'] - include_recipe "postgresql::apt_pgdg_postgresql" - resources("file[remove deprecated Pitti PPA apt repository]").run_action(:delete) - resources("apt_repository[apt.postgresql.org]").run_action(:add) - - node['postgresql']['client']['packages'].each do |pkg| - package pkg do - action :nothing - end.run_action(:install) - end - + if node['postgresql']['enable_pgdg_apt'] && platform_family?('debian') + include_recipe 'postgresql::apt_pgdg_postgresql' + resources('apt_repository[apt.postgresql.org]').run_action(:add) end - include_recipe "postgresql::client" + include_recipe 'postgresql::client' - node['postgresql']['client']['packages'].each do |pkg| - package pkg do - action :nothing - end.run_action(:install) - end + package node['postgresql']['client']['packages'] do + action :nothing + end.run_action(:install) begin - chef_gem "pg" + chef_gem 'pg' do + compile_time true + version node['postgresql']['pg_gem']['version'] if node['postgresql']['pg_gem']['version'] + end rescue Gem::Installer::ExtensionBuildError, Mixlib::ShellOut::ShellCommandFailed => e # Are we an omnibus install? - raise if RbConfig.ruby.scan(%r{(chef|opscode)}).empty? + raise if RbConfig.ruby.scan(/(chef|opscode)/).empty? # Still here, must be omnibus. Lets make this thing install! Chef::Log.warn 'Failed to properly build pg gem. Forcing properly linking and retrying (omnibus fix)' - gem_dir = e.message.scan(%r{will remain installed in ([^ ]+)}).flatten.first + gem_dir = e.message.scan(/will remain installed in ([^ ]+)/).flatten.first raise unless gem_dir gem_name = File.basename(gem_dir) ext_dir = File.join(gem_dir, 'ext') diff --git a/cookbooks/postgresql/recipes/server.rb b/cookbooks/postgresql/recipes/server.rb index 211d170..58c5419 100644 --- a/cookbooks/postgresql/recipes/server.rb +++ b/cookbooks/postgresql/recipes/server.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: server # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,25 +16,23 @@ # limitations under the License. # -include_recipe "postgresql::ca_certificates" - ::Chef::Recipe.send(:include, OpenSSLCookbook::RandomPassword) -include_recipe "postgresql::client" +include_recipe 'postgresql::client' # randomly generate postgres password, unless using solo - see README if Chef::Config[:solo] - missing_attrs = %w{ + missing_attrs = %w( postgres - }.select do |attr| + ).select do |attr| node['postgresql']['password'][attr].nil? end.map { |attr| "node['postgresql']['password']['#{attr}']" } - if !missing_attrs.empty? + unless missing_attrs.empty? Chef::Log.fatal([ - "You must set #{missing_attrs.join(', ')} in chef-solo mode.", - "For more information, see https://github.com/opscode-cookbooks/postgresql#chef-solo-note" - ].join(' ')) + "You must set #{missing_attrs.join(', ')} in chef-solo mode.", + 'For more information, see https://github.com/opscode-cookbooks/postgresql#chef-solo-note', + ].join(' ')) raise end else @@ -44,7 +43,7 @@ else # useful if it weren't saved as clear text in Chef Server for later # retrieval. unless node.key?('postgresql') && node['postgresql'].key?('password') && node['postgresql']['password'].key?('postgres') - node.set_unless['postgresql']['password']['postgres'] = random_password(length: 20, mode: :base64) + node.normal_unless['postgresql']['password']['postgres'] = random_password(length: 20, mode: :base64) node.save end end @@ -52,30 +51,29 @@ end # Include the right "family" recipe for installing the server # since they do things slightly differently. case node['platform_family'] -when "rhel", "fedora" - node.set['postgresql']['dir'] = "/var/lib/pgsql/#{node['postgresql']['version']}/data" - node.set['postgresql']['config']['data_directory'] = "/var/lib/pgsql/#{node['postgresql']['version']}/data" - include_recipe "postgresql::server_redhat" -when "debian" - node.set['postgresql']['config']['data_directory'] = "/var/lib/postgresql/#{node['postgresql']['version']}/main" - include_recipe "postgresql::server_debian" +when 'rhel', 'fedora' + node.normal['postgresql']['dir'] = "/var/lib/pgsql/#{node['postgresql']['version']}/data" + node.normal['postgresql']['config']['data_directory'] = "/var/lib/pgsql/#{node['postgresql']['version']}/data" + include_recipe 'postgresql::server_redhat' +when 'debian' + node.normal['postgresql']['config']['data_directory'] = "/var/lib/postgresql/#{node['postgresql']['version']}/main" + include_recipe 'postgresql::server_debian' when 'suse' - node.set['postgresql']['config']['data_directory'] = node['postgresql']['dir'] - include_recipe "postgresql::server_redhat" + node.normal['postgresql']['config']['data_directory'] = node['postgresql']['dir'] + include_recipe 'postgresql::server_redhat' end # Versions prior to 9.2 do not have a config file option to set the SSL # key and cert path, and instead expect them to be in a specific location. -if node['postgresql']['version'].to_f < 9.2 && node['postgresql']['config'].attribute?('ssl_cert_file') - link ::File.join(node['postgresql']['config']['data_directory'], 'server.crt') do - to node['postgresql']['config']['ssl_cert_file'] - end + +link ::File.join(node['postgresql']['config']['data_directory'], 'server.crt') do + to node['postgresql']['config']['ssl_cert_file'] + only_if { node['postgresql']['version'].to_f < 9.2 && node['postgresql']['config'].attribute?('ssl_cert_file') } end -if node['postgresql']['version'].to_f < 9.2 && node['postgresql']['config'].attribute?('ssl_key_file') - link ::File.join(node['postgresql']['config']['data_directory'], 'server.key') do - to node['postgresql']['config']['ssl_key_file'] - end +link ::File.join(node['postgresql']['config']['data_directory'], 'server.key') do + to node['postgresql']['config']['ssl_key_file'] + only_if { node['postgresql']['version'].to_f < 9.2 && node['postgresql']['config'].attribute?('ssl_key_file') } end # NOTE: Consider two facts before modifying "assign-postgres-password": @@ -86,7 +84,7 @@ end # setting the same password. This chef recipe doesn't have access to # the plain text password, and testing the encrypted (md5 digest) # version is not straight-forward. -bash "assign-postgres-password" do +bash 'assign-postgres-password' do user 'postgres' code <<-EOH echo "ALTER ROLE postgres ENCRYPTED PASSWORD \'#{node['postgresql']['password']['postgres']}\';" | psql -p #{node['postgresql']['config']['port']} diff --git a/cookbooks/postgresql/recipes/server_conf.rb b/cookbooks/postgresql/recipes/server_conf.rb index 39af1a6..f9d284f 100644 --- a/cookbooks/postgresql/recipes/server_conf.rb +++ b/cookbooks/postgresql/recipes/server_conf.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: server # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,37 +21,35 @@ change_notify = node['postgresql']['server']['config_change_notify'] # There are some configuration items which depend on correctly evaluating the intended version being installed if node['platform_family'] == 'debian' - node.set['postgresql']['config']['hba_file'] = "/etc/postgresql/#{node['postgresql']['version']}/main/pg_hba.conf" - node.set['postgresql']['config']['ident_file'] = "/etc/postgresql/#{node['postgresql']['version']}/main/pg_ident.conf" - node.set['postgresql']['config']['external_pid_file'] = "/var/run/postgresql/#{node['postgresql']['version']}-main.pid" + node.normal['postgresql']['config']['hba_file'] = "/etc/postgresql/#{node['postgresql']['version']}/main/pg_hba.conf" + node.normal['postgresql']['config']['ident_file'] = "/etc/postgresql/#{node['postgresql']['version']}/main/pg_ident.conf" + node.normal['postgresql']['config']['external_pid_file'] = "/var/run/postgresql/#{node['postgresql']['version']}-main.pid" if node['postgresql']['version'].to_f < 9.3 - node.set['postgresql']['config']['unix_socket_directory'] = '/var/run/postgresql' + node.normal['postgresql']['config']['unix_socket_directory'] = '/var/run/postgresql' else - node.set['postgresql']['config']['unix_socket_directories'] = '/var/run/postgresql' + node.normal['postgresql']['config']['unix_socket_directories'] = '/var/run/postgresql' end - node.set['postgresql']['config']['max_fsm_pages'] = 153600 if node['postgresql']['version'].to_f < 8.4 - if node['postgresql']['config']['ssl'] - node.set['postgresql']['config']['ssl_cert_file'] = '/etc/ssl/certs/ssl-cert-snakeoil.pem' if node['postgresql']['version'].to_f >= 9.2 - node.set['postgresql']['config']['ssl_key_file'] = '/etc/ssl/private/ssl-cert-snakeoil.key'if node['postgresql']['version'].to_f >= 9.2 + node.normal['postgresql']['config']['ssl_cert_file'] = '/etc/ssl/certs/ssl-cert-snakeoil.pem' if node['postgresql']['version'].to_f >= 9.2 + node.normal['postgresql']['config']['ssl_key_file'] = '/etc/ssl/private/ssl-cert-snakeoil.key' if node['postgresql']['version'].to_f >= 9.2 end end template "#{node['postgresql']['dir']}/postgresql.conf" do - source "postgresql.conf.erb" - owner "postgres" - group "postgres" - mode 0600 + source 'postgresql.conf.erb' + owner 'postgres' + group 'postgres' + mode '0600' notifies change_notify, 'service[postgresql]', :immediately end template "#{node['postgresql']['dir']}/pg_hba.conf" do - source "pg_hba.conf.erb" - owner "postgres" - group "postgres" - mode 00600 + source 'pg_hba.conf.erb' + owner 'postgres' + group 'postgres' + mode '0600' notifies change_notify, 'service[postgresql]', :immediately end diff --git a/cookbooks/postgresql/recipes/server_debian.rb b/cookbooks/postgresql/recipes/server_debian.rb index 65da795..292b805 100644 --- a/cookbooks/postgresql/recipes/server_debian.rb +++ b/cookbooks/postgresql/recipes/server_debian.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: server # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,19 +16,15 @@ # limitations under the License. # -include_recipe "postgresql::client" +include_recipe 'postgresql::client' -node['postgresql']['server']['packages'].each do |pg_pack| +package node['postgresql']['server']['packages'] - package pg_pack +include_recipe 'postgresql::server_conf' -end - -include_recipe "postgresql::server_conf" - -service "postgresql" do +service 'postgresql' do service_name node['postgresql']['server']['service_name'] - supports :restart => true, :status => true, :reload => true + supports restart: true, status: true, reload: true action [:enable, :start] end diff --git a/cookbooks/postgresql/recipes/server_redhat.rb b/cookbooks/postgresql/recipes/server_redhat.rb index 1c0834c..45c9309 100644 --- a/cookbooks/postgresql/recipes/server_redhat.rb +++ b/cookbooks/postgresql/recipes/server_redhat.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe:: server # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,7 +16,7 @@ # limitations under the License. # -include_recipe "postgresql::client" +include_recipe 'postgresql::client' svc_name = node['postgresql']['server']['service_name'] initdb_locale = node['postgresql']['initdb_locale'] @@ -25,44 +26,39 @@ shortver = node['postgresql']['version'].split('.').join # Create a group and user like the package will. # Otherwise the templates fail. -group "postgres" do - gid 26 +group 'postgres' do + gid node['postgresql']['gid'] end -user "postgres" do - shell "/bin/bash" - comment "PostgreSQL Server" - home "/var/lib/pgsql" - gid "postgres" +user 'postgres' do + shell '/bin/bash' + comment 'PostgreSQL Server' + home '/var/lib/pgsql' + gid 'postgres' system true - uid 26 - supports :manage_home => false + uid node['postgresql']['uid'] + manage_home false end directory node['postgresql']['config']['data_directory'] do - owner "postgres" - group "postgres" + owner 'postgres' + group 'postgres' recursive true action :create + mode '0700' end -node['postgresql']['server']['packages'].each do |pg_pack| - - package pg_pack - -end +package node['postgresql']['server']['packages'] # If using PGDG, add symlinks so that downstream commands all work -if node['postgresql']['enable_pgdg_yum'] == true +if node['postgresql']['enable_pgdg_yum'] == true || node['postgresql']['use_pgdg_packages'] == true [ "postgresql#{shortver}-setup", - "postgresql#{shortver}-check-db-dir" + "postgresql#{shortver}-check-db-dir", ].each do |cmd| - link "/usr/bin/#{cmd}" do to "/usr/pgsql-#{node['postgresql']['version']}/bin/#{cmd}" end - end end @@ -71,22 +67,44 @@ end unless node['postgresql']['server']['init_package'] == 'systemd' - directory "/etc/sysconfig/pgsql" do - mode "0644" + directory '/etc/sysconfig/pgsql' do + mode '0644' recursive true action :create end template "/etc/sysconfig/pgsql/#{svc_name}" do - source "pgsql.sysconfig.erb" - mode "0644" - notifies :restart, "service[postgresql]", :delayed + source 'pgsql.sysconfig.erb' + mode '0644' + notifies :restart, 'service[postgresql]', :delayed end end if node['postgresql']['server']['init_package'] == 'systemd' + if node['platform_family'] == 'rhel' + + template_path = if node['postgresql']['use_pgdg_packages'] + "/etc/systemd/system/postgresql-#{node['postgresql']['version']}.service" + else + '/etc/systemd/system/postgresql.service' + end + + template template_path do + source 'postgresql.service.erb' + owner 'root' + group 'root' + mode '0644' + notifies :run, 'execute[systemctl-reload]', :immediately + notifies :reload, 'service[postgresql]', :delayed + end + execute 'systemctl-reload' do + command 'systemctl daemon-reload' + action :nothing + end + end + case node['platform_family'] when 'suse' execute "initdb -d #{node['postgresql']['dir']}" do @@ -99,7 +117,7 @@ if node['postgresql']['server']['init_package'] == 'systemd' end end -elsif (!platform_family?("suse") && node['postgresql']['version'].to_f <= 9.3) +elsif !platform_family?('suse') && node['postgresql']['version'].to_f <= 9.3 execute "/sbin/service #{svc_name} initdb #{initdb_locale}" do not_if { ::File.exist?("#{node['postgresql']['config']['data_directory']}/PG_VERSION") } @@ -113,10 +131,10 @@ else end -service "postgresql" do +service 'postgresql' do service_name svc_name - supports :restart => true, :status => true, :reload => true + supports restart: true, status: true, reload: true action [:enable, :start] end -include_recipe "postgresql::server_conf" +include_recipe 'postgresql::server_conf' diff --git a/cookbooks/postgresql/recipes/yum_pgdg_postgresql.rb b/cookbooks/postgresql/recipes/yum_pgdg_postgresql.rb index 97faeb8..0e03b72 100644 --- a/cookbooks/postgresql/recipes/yum_pgdg_postgresql.rb +++ b/cookbooks/postgresql/recipes/yum_pgdg_postgresql.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true # -# Cookbook Name:: postgresql +# Cookbook:: postgresql # Recipe::yum_pgdg_postgresql # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,16 +22,20 @@ rpm_platform = node['platform'] rpm_platform_version = node['platform_version'].to_f.to_i.to_s arch = node['kernel']['machine'] +pg_version = node['postgresql']['version'] +pgdg_setup = node['postgresql']['pgdg']['repo_rpm_url'][pg_version][rpm_platform][rpm_platform_version][arch] +pgdg_package = pgdg_setup['package'] +pgdg_repository = pgdg_setup['url'] # Download the PGDG repository RPM as a local file -remote_file "#{Chef::Config[:file_cache_path]}/#{node[:postgresql][:pgdg][:repo_rpm_url][node[:postgresql][:version]][rpm_platform][rpm_platform_version][arch][:package]}" do - source "#{node[:postgresql][:pgdg][:repo_rpm_url][node[:postgresql][:version]][rpm_platform][rpm_platform_version][arch][:url]}#{node[:postgresql][:pgdg][:repo_rpm_url][node[:postgresql][:version]][rpm_platform][rpm_platform_version][arch][:package]}" - mode "0644" +remote_file "#{Chef::Config[:file_cache_path]}/#{pgdg_package}" do + source "#{pgdg_repository}#{pgdg_package}" + mode '0644' end # Install the PGDG repository RPM from the local file -package "#{node[:postgresql][:pgdg][:repo_rpm_url][node[:postgresql][:version]][rpm_platform][rpm_platform_version][arch][:package]}" do +package pgdg_package.to_s do provider Chef::Provider::Package::Rpm - source "#{Chef::Config[:file_cache_path]}/#{node[:postgresql][:pgdg][:repo_rpm_url][node[:postgresql][:version]][rpm_platform][rpm_platform_version][arch][:package]}" + source "#{Chef::Config[:file_cache_path]}/#{pgdg_package}" action :install end diff --git a/cookbooks/postgresql/resources/extension.rb b/cookbooks/postgresql/resources/extension.rb new file mode 100644 index 0000000..5a279c3 --- /dev/null +++ b/cookbooks/postgresql/resources/extension.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true +# +# Cookbook:: postgresql +# Resource:: extension +# +# 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. +# + +include Opscode::PostgresqlHelpers + +# name property should take the form: +# database/extension + +property :database, String, + required: true, + default: lazy { name.scan(%r{\A[^/]+(?=/)}).first } + +property :extension, String, + required: true, + default: lazy { name.scan(%r{(?<=/)[^/]+\Z}).first } + +action :create do + bash "CREATE EXTENSION #{name}" do + code psql("CREATE EXTENSION IF NOT EXISTS \"#{extension}\"") + user 'postgres' + action :run + not_if { extension_installed? } + end +end + +action :drop do + bash "DROP EXTENSION #{name}" do + code psql("DROP EXTENSION IF EXISTS \"#{extension}\"") + user 'postgres' + action :run + only_if { extension_installed? } + end +end + +def psql(query) + "psql -d #{database} <<< '\\set ON_ERROR_STOP on\n#{query};'" +end + +def extension_installed? + query = "SELECT 'installed' FROM pg_extension WHERE extname = '#{extension}';" + !(execute_sql(query, database) =~ /^installed$/).nil? +end diff --git a/cookbooks/postgresql/templates/default/pg_hba.conf.erb b/cookbooks/postgresql/templates/default/pg_hba.conf.erb index 55d757d..0c38c78 100644 --- a/cookbooks/postgresql/templates/default/pg_hba.conf.erb +++ b/cookbooks/postgresql/templates/default/pg_hba.conf.erb @@ -18,7 +18,7 @@ <% node['postgresql']['pg_hba'].each do |auth| -%> <% if auth[:comment] %> -<%= auth[:comment] %> +# <%= auth[:comment] %> <% end %> <% if auth[:addr] %> <%= auth[:type].ljust(7) %> <%= auth[:db].ljust(15) %> <%= auth[:user].ljust(15) %> <%= auth[:addr].ljust(23) %> <%= auth[:method] %> diff --git a/cookbooks/postgresql/templates/default/postgresql.service.erb b/cookbooks/postgresql/templates/default/postgresql.service.erb new file mode 100644 index 0000000..00ffff4 --- /dev/null +++ b/cookbooks/postgresql/templates/default/postgresql.service.erb @@ -0,0 +1,10 @@ +[Service] +<% if node['postgresql']['use_pgdg_packages'] %> +.include /usr/lib/systemd/system/postgresql-<%= node['postgresql']['version'] %>.service +<% else %> +.include /usr/lib/systemd/system/postgresql.service +<% end %> + +Environment= +Environment=PGPORT=<%= node['postgresql']['config']['port'] %> +Environment=PGDATA=<%= node['postgresql']['config']['data_directory'] %> diff --git a/cookbooks/wordpress/README.md b/cookbooks/wordpress/README.md index 1a99f24..3e42565 100644 --- a/cookbooks/wordpress/README.md +++ b/cookbooks/wordpress/README.md @@ -51,7 +51,6 @@ Attributes * `node['wordpress']['db']['port']` - Port of the WordPress MySQL database. * `node['wordpress']['db']['charset']` - [Character set](http://dev.mysql.com/doc/refman/5.7/en/charset-charsets.html) of the WordPress MySQL database tables. Defaults to 'utf8'. * `node['wordpress']['db']['collate']` - [Collation](http://dev.mysql.com/doc/refman/5.7/en/charset-collation-effect.html) of the WordPress MySQL database tables. -* `node['wordpress']['db']['mysql_version']` - Version of MySQL to install (for supporting community cookbook version 6+) * `node['wordpress']['allow_multisite']` - Enable [multisite](http://codex.wordpress.org/Create_A_Network) features (default: false). * `node['wordpress']['wp_config_options']` - A hash of options to define in wp_config.php, output as key value pairs into a PHP constant e.g. `define( '<%= @key %>', <%= @value %> );`. Note: for values you will need to add single quotes around text but omit them for booleans and numbers. (default: {}). diff --git a/cookbooks/wordpress/attributes/default.rb b/cookbooks/wordpress/attributes/default.rb index 873753d..92d9b86 100644 --- a/cookbooks/wordpress/attributes/default.rb +++ b/cookbooks/wordpress/attributes/default.rb @@ -35,25 +35,6 @@ default['wordpress']['db']['host'] = 'localhost' default['wordpress']['db']['port'] = '3306' # Must be a string default['wordpress']['db']['charset'] = 'utf8' default['wordpress']['db']['collate'] = '' -case node['platform'] -when 'ubuntu' - case node['platform_version'] - when '10.04' - default['wordpress']['db']['mysql_version'] = '5.1' - else - default['wordpress']['db']['mysql_version'] = '5.5' - end -when 'centos', 'redhat', 'amazon', 'scientific' - if node['platform_version'].to_i < 6 - default['wordpress']['db']['mysql_version'] = '5.0' - elsif node['platform_version'].to_i < 7 - default['wordpress']['db']['mysql_version'] = '5.1' - else - default['wordpress']['db']['mysql_version'] = '5.5' - end -else - default['wordpress']['db']['mysql_version'] = '5.5' -end default['wordpress']['allow_multisite'] = false diff --git a/cookbooks/wordpress/metadata.rb b/cookbooks/wordpress/metadata.rb index 344c6c5..e7e8515 100644 --- a/cookbooks/wordpress/metadata.rb +++ b/cookbooks/wordpress/metadata.rb @@ -16,7 +16,7 @@ end depends "apache2", ">= 2.0.0" depends "database", ">= 1.6.0" depends "mysql", ">= 6.0" -depends "mysql2_chef_gem", "~> 1.0.1" +depends "mysql2_chef_gem", ">= 1.0.1" depends "build-essential" depends "iis", ">= 1.6.2" depends "tar", ">= 0.3.1" diff --git a/cookbooks/wordpress/recipes/database.rb b/cookbooks/wordpress/recipes/database.rb index 3f61d78..cdbac48 100644 --- a/cookbooks/wordpress/recipes/database.rb +++ b/cookbooks/wordpress/recipes/database.rb @@ -44,7 +44,6 @@ if is_local_host? db['host'] mysql_service db['instance_name'] do port db['port'] - version db['mysql_version'] initial_root_password db['root_password'] action [:create, :start] end @@ -52,6 +51,11 @@ if is_local_host? db['host'] socket = "/var/run/mysql-#{db['instance_name']}/mysqld.sock" if node['platform_family'] == 'debian' + directory "/var/run/mysqld" do + action :create + owner "mysql" + group "mysql" + end link '/var/run/mysqld/mysqld.sock' do to socket not_if 'test -f /var/run/mysqld/mysqld.sock' diff --git a/data_bags/credentials/mastodon.json b/data_bags/credentials/mastodon.json new file mode 100644 index 0000000..093ee62 --- /dev/null +++ b/data_bags/credentials/mastodon.json @@ -0,0 +1,45 @@ +{ + "id": "mastodon", + "paperclip_secret": { + "encrypted_data": "/rai6Zj08FBtOdhpwCuRU6gSvBeYrQ3IiZ+NKCThOB4k7TrJ5/DZ+Jl3Pz1l\n3yJ1QfVf8w5dBLiGFmlimEu0uMi4fQohTdpi4pIczARneH5M9pN6jY2ZkMoy\nnmuO95bKGgX4GZi33F3T+XwwEQ3LPm+a6Ml2S7Ycffcx2xbNqTH5e/lWX3qF\ngDYZZR+R9g0GSQ2L+pZAdRMxDZz/WufBSA==\n", + "iv": "nsdbLOcJOHGAt4T/R+bwDA==\n", + "version": 1, + "cipher": "aes-256-cbc" + }, + "secret_key_base": { + "encrypted_data": "uvrKsWy+34xFMoC82n6nx2bjZyx1qseHm3mg7FvC5XzyTomc3km0jdr0cGo8\ngydAwZU5rOB3vX+eA2vqipRbROUboOoSahAxfzDI3187NgBhZgcootsmVqaB\njPs0y1un2C6YCF8owT/eTMnqta+Sfzyc+1fWSYQtoWVgoEejc54gyGB+vWB0\nxiLoPsDRIYM1LstGu8AdKP77iDYRpAsYsA==\n", + "iv": "anBxz01wpEH2iNVWktADYg==\n", + "version": 1, + "cipher": "aes-256-cbc" + }, + "otp_secret": { + "encrypted_data": "q8BLFwwZU/hoinL8Aizd27fWj2SIBFLmM5n/LBBuUt4q4zuauA4591fkTlPH\n1vihDK9hE4xl90V1t6IIwj3IpBq9w53rfToJrBwxD1l63j41bEvJ39qr8gsh\nbH4xT/rzsyp5cWXrakCDIOuOHBuoFDlJetIY2BLDLXbqDEwFG4CI5YoPn0gV\nCPCNmRKITulem2YjqnGswFd/tvvR2X/w5Q==\n", + "iv": "BMjOeH9mAW0J3pRJAMFlKw==\n", + "version": 1, + "cipher": "aes-256-cbc" + }, + "aws_access_key_id": { + "encrypted_data": "wSevnv6+7QbJb4HNEaTCXzL+QzcES+QZFjS6gcPxP+JvAnQhhNmf0FwlqGP2\n4gXN\n", + "iv": "mVIoUHFYRQUY/u8ZqCdd/w==\n", + "version": 1, + "cipher": "aes-256-cbc" + }, + "aws_secret_access_key": { + "encrypted_data": "ZNBp33pfndINgpyiGpM4nQc1/TeSg2KmeOWGJTaxf6KggKnqN+ZIcW/FhWTN\n0IE1Z4cxBJpzK/voCbeEhZC5Sg==\n", + "iv": "p1lGI0p+EOpGcyzATvofCQ==\n", + "version": 1, + "cipher": "aes-256-cbc" + }, + "smtp_user_name": { + "encrypted_data": "ZbcFLsjQOtrY+5WoK5CFgOywm2HMO5aivoAXLEKIXCh/5NpOJuMWr13RSbhu\n31b/\n", + "iv": "t1+hKaEUwRee3LGQ6H0a7g==\n", + "version": 1, + "cipher": "aes-256-cbc" + }, + "smtp_password": { + "encrypted_data": "EDmXwQjtqpJpClc+vbe8L+h/599gcTIvIzq2p+T/cY8+qUAYQzZpsvboJiVI\n43YQxYNtJ7Lm784QDhRdbTAEtw==\n", + "iv": "cQq5ym70FasDRIMQM5CLSg==\n", + "version": 1, + "cipher": "aes-256-cbc" + } +} \ No newline at end of file diff --git a/site-cookbooks/5apps-hubot/metadata.rb b/site-cookbooks/5apps-hubot/metadata.rb index 77446db..b445a18 100644 --- a/site-cookbooks/5apps-hubot/metadata.rb +++ b/site-cookbooks/5apps-hubot/metadata.rb @@ -7,5 +7,6 @@ long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) version '0.1.0' depends 'kosmos-nodejs' -depends 'application' -depends 'application_nodejs' +depends 'application_javascript' +depends 'application_git' +depends 'firewall' diff --git a/site-cookbooks/5apps-hubot/recipes/xmpp_botka.rb b/site-cookbooks/5apps-hubot/recipes/xmpp_botka.rb index 2805424..0df8447 100644 --- a/site-cookbooks/5apps-hubot/recipes/xmpp_botka.rb +++ b/site-cookbooks/5apps-hubot/recipes/xmpp_botka.rb @@ -7,59 +7,99 @@ # All rights reserved - Do Not Redistribute # -firewall_rule 'hubot_express_botka_xmpp' do - port 8082 - protocol :tcp - command :allow +unless node.chef_environment == "development" + include_recipe "firewall" + firewall_rule 'hubot_express_botka_xmpp' do + port 8082 + protocol :tcp + command :allow + end +end + +group "hubot" do + gid 48268 +end + +user "hubot" do + system true + manage_home true + comment "hubot user" + uid 48268 + gid 48268 + + shell "/bin/bash" end botka_xmpp_data_bag_item = Chef::EncryptedDataBagItem.load('credentials', '5apps_botka_xmpp') -application "botka_xmpp" do - path "/srv/botka_xmpp" +botka_xmpp_path = "/opt/botka_xmpp" +application botka_xmpp_path do owner "hubot" group "hubot" - action :deploy - - before_restart do - file "#{new_resource.release_path}/external-scripts.json" do - mode "0640" - owner "hubot" - group "hubot" - content [ - "hubot-help", - "hubot-remotestorage-logger" - ].to_json - end - - file "#{new_resource.release_path}/external-scripts.json" do - mode "0640" - owner "hubot" - group "hubot" - content [].to_json - end + git do + user "hubot" + group "hubot" + repository "https://github.com/67P/botka.git" + revision "master" end - repository "https://github.com/67P/botka.git" - revision "master" + file "external-scripts.json" do + mode "0640" + owner "hubot" + group "hubot" + content [ + "hubot-help", + "hubot-remotestorage-logger" + ].to_json + end - nodejs do - entry_point "/srv/botka_xmpp/current/bin/hubot -a xmpp --name botka" - # Use our own systemd service that depends on redis-server - template "nodejs.systemd.service.erb" - environment "HUBOT_XMPP_USERNAME" => "botka@5apps.com/hubot", - "HUBOT_XMPP_PASSWORD" => botka_xmpp_data_bag_item['password'], - "HUBOT_XMPP_ROOMS" => "5info@muc.5apps.com,5ops@muc.5apps.com,core@muc.5apps.com,deploy@muc.5apps.com,storage@muc.5apps.com,watercooler@muc.5apps.com,hilti@muc.5apps.com,gymapp@muc.5apps.com", - "HUBOT_XMPP_HOST" => "xmpp.5apps.com", - "HUBOT_RSS_PRINTSUMMARY" => "false", - "EXPRESS_PORT" => "8082", - "HUBOT_RSS_HEADER" => "Update:", - "HUBOT_AUTH_ADMIN" => "basti,garret,greg", - "REDIS_URL" => "redis://localhost:6379/5apps_botka_xmpp", - "RS_LOGGER_USER" => "5apps@5apps.com", - "RS_LOGGER_TOKEN" => botka_xmpp_data_bag_item['rs_logger_token'], - "RS_LOGGER_SERVER_NAME" => "5apps", - "WEBHOOK_TOKEN" => botka_xmpp_data_bag_item['webhook_token'] + file "external-scripts.json" do + mode "0640" + owner "hubot" + group "hubot" + content [].to_json + end + + npm_install do + user "hubot" + end + + execute "systemctl daemon-reload" do + command "systemctl daemon-reload" + action :nothing + end + + template "/lib/systemd/system/botka_xmpp_nodejs.service" do + source 'nodejs.systemd.service.erb' + owner 'root' + group 'root' + mode '0644' + variables( + :user => "hubot", + :group => "hubot", + :app_dir => botka_xmpp_path, + :entry => "#{botka_xmpp_path}/bin/hubot -a xmpp --name botka", + :environment => { "HUBOT_XMPP_USERNAME" => "botka@5apps.com/hubot", + "HUBOT_XMPP_PASSWORD" => botka_xmpp_data_bag_item['password'], + "HUBOT_XMPP_ROOMS" => "5info@muc.5apps.com,5ops@muc.5apps.com,core@muc.5apps.com,deploy@muc.5apps.com,storage@muc.5apps.com,watercooler@muc.5apps.com,hilti@muc.5apps.com,gymapp@muc.5apps.com", + "HUBOT_XMPP_HOST" => "xmpp.5apps.com", + "HUBOT_RSS_PRINTSUMMARY" => "false", + "EXPRESS_PORT" => "8082", + "HUBOT_RSS_HEADER" => "Update:", + "HUBOT_AUTH_ADMIN" => "basti,garret,greg", + "REDIS_URL" => "redis://localhost:6379/5apps_botka_xmpp", + "RS_LOGGER_USER" => "5apps@5apps.com", + "RS_LOGGER_TOKEN" => botka_xmpp_data_bag_item['rs_logger_token'], + "RS_LOGGER_SERVER_NAME" => "5apps", + "WEBHOOK_TOKEN" => botka_xmpp_data_bag_item['webhook_token'] } + ) + + notifies :run, "execute[systemctl daemon-reload]", :delayed + notifies :restart, "service[botka_xmpp_nodejs]", :delayed + end + + service "botka_xmpp_nodejs" do + action [:enable, :start] end end diff --git a/site-cookbooks/5apps-hubot/recipes/xmpp_schlupp.rb b/site-cookbooks/5apps-hubot/recipes/xmpp_schlupp.rb index d43eb5d..3d5227a 100644 --- a/site-cookbooks/5apps-hubot/recipes/xmpp_schlupp.rb +++ b/site-cookbooks/5apps-hubot/recipes/xmpp_schlupp.rb @@ -7,62 +7,99 @@ # All rights reserved - Do Not Redistribute # -firewall_rule 'hubot_express_schlupp_xmpp' do - port 8083 - protocol :tcp - command :allow +unless node.chef_environment == "development" + include_recipe "firewall" + firewall_rule 'hubot_express_schlupp_xmpp' do + port 8083 + protocol :tcp + command :allow + end +end + +group "hubot" do + gid 48268 +end + +user "hubot" do + system true + manage_home true + comment "hubot user" + uid 48268 + gid 48268 + shell "/bin/bash" end schlupp_xmpp_data_bag_item = Chef::EncryptedDataBagItem.load('credentials', '5apps_schlupp_xmpp') -application "schlupp_xmpp" do - path "/srv/schlupp_xmpp" +schlupp_xmpp_path = "/opt/schlupp_xmpp" +application schlupp_xmpp_path do owner "hubot" group "hubot" - action :deploy - - before_restart do - # No hubot-remotestorage-logger, botka takes care of that - file "#{new_resource.release_path}/external-scripts.json" do - mode "0640" - owner "hubot" - group "hubot" - content [ - "hubot-auth", - "hubot-help", - "hubot-redis-brain", - "hubot-rules", - "hubot-shipit", - "hubot-plusplus", - "hubot-tell", - "hubot-seen", - "hubot-rss-reader", - "hubot-incoming-webhook", - "hubot-yubikey-invalidation" - ].to_json - end + git "git@gitlab.com:5apps/schlupp.git" do + user "hubot" + group "hubot" + revision "master" + deploy_key schlupp_xmpp_data_bag_item['deploy_key'] end - repository "git@gitlab.com:5apps/schlupp.git" - revision "master" - deploy_key schlupp_xmpp_data_bag_item['deploy_key'] + file "external-scripts.json" do + mode "0640" + owner "hubot" + group "hubot" + content [ + "hubot-auth", + "hubot-help", + "hubot-redis-brain", + "hubot-rules", + "hubot-shipit", + "hubot-plusplus", + "hubot-tell", + "hubot-seen", + "hubot-rss-reader", + "hubot-incoming-webhook", + "hubot-yubikey-invalidation" + ].to_json + end - nodejs do - entry_point "/srv/schlupp_xmpp/current/bin/hubot -a xmpp --name schlupp" - # Use our own systemd service that depends on redis-server - template "nodejs.systemd.service.erb" - environment "HUBOT_XMPP_USERNAME" => "schlupp@5apps.com/hubot", - "HUBOT_XMPP_PASSWORD" => schlupp_xmpp_data_bag_item['password'], - "HUBOT_XMPP_ROOMS" => "5info@muc.5apps.com,5ops@muc.5apps.com,core@muc.5apps.com,deploy@muc.5apps.com,storage@muc.5apps.com,watercooler@muc.5apps.com,hilti@muc.5apps.com,test@muc.5apps.com,gymapp@muc.5apps.com", - "HUBOT_XMPP_HOST" => "xmpp.5apps.com", - "HUBOT_RSS_PRINTSUMMARY" => "false", - "EXPRESS_PORT" => "8083", - "HUBOT_RSS_HEADER" => "Update:", - "HUBOT_AUTH_ADMIN" => "basti,garret,greg", - "REDIS_URL" => "redis://localhost:6379/5apps_schlupp_xmpp", - "RS_OPS_TOKEN" => schlupp_xmpp_data_bag_item['rs_ops_token'], - "WEBHOOK_TOKEN" => schlupp_xmpp_data_bag_item['webhook_token'], - "AIRTABLE_API_KEY" => schlupp_xmpp_data_bag_item['airtable_api_key'] + npm_install do + user "hubot" + end + + execute "systemctl daemon-reload" do + command "systemctl daemon-reload" + action :nothing + end + + template "/lib/systemd/system/schlupp_xmpp_nodejs.service" do + source 'nodejs.systemd.service.erb' + owner 'root' + group 'root' + mode '0644' + variables( + :user => "hubot", + :group => "hubot", + :app_dir => schlupp_xmpp_path, + :entry => "#{schlupp_xmpp_path}/bin/hubot -a xmpp --name schlupp", + :environment => { "HUBOT_XMPP_USERNAME" => "schlupp@5apps.com/hubot", + "HUBOT_XMPP_PASSWORD" => schlupp_xmpp_data_bag_item['password'], + "HUBOT_XMPP_ROOMS" => "5info@muc.5apps.com,5ops@muc.5apps.com,core@muc.5apps.com,deploy@muc.5apps.com,storage@muc.5apps.com,watercooler@muc.5apps.com,hilti@muc.5apps.com,test@muc.5apps.com,gymapp@muc.5apps.com", + "HUBOT_XMPP_HOST" => "xmpp.5apps.com", + "HUBOT_RSS_PRINTSUMMARY" => "false", + "EXPRESS_PORT" => "8083", + "HUBOT_RSS_HEADER" => "Update:", + "HUBOT_AUTH_ADMIN" => "basti,garret,greg", + "REDIS_URL" => "redis://localhost:6379/5apps_schlupp_xmpp", + "RS_OPS_TOKEN" => schlupp_xmpp_data_bag_item['rs_ops_token'], + "WEBHOOK_TOKEN" => schlupp_xmpp_data_bag_item['webhook_token'], + "AIRTABLE_API_KEY" => schlupp_xmpp_data_bag_item['airtable_api_key'] } + ) + + notifies :run, "execute[systemctl daemon-reload]", :delayed + notifies :restart, "service[schlupp_xmpp_nodejs]", :delayed + end + + service "schlupp_xmpp_nodejs" do + action [:enable, :start] end end diff --git a/site-cookbooks/5apps-xmpp_server/recipes/default.rb b/site-cookbooks/5apps-xmpp_server/recipes/default.rb index 7332052..6a4bfa4 100644 --- a/site-cookbooks/5apps-xmpp_server/recipes/default.rb +++ b/site-cookbooks/5apps-xmpp_server/recipes/default.rb @@ -7,10 +7,13 @@ # All rights reserved - Do Not Redistribute # -firewall_rule "xmpp" do - port [5222, 5269] - protocol :tcp - command :allow +unless node.chef_environment == "development" + include_recipe "firewall" + firewall_rule "xmpp" do + port [5222, 5269] + protocol :tcp + command :allow + end end apt_repository "prosody" do diff --git a/site-cookbooks/backup/recipes/default.rb b/site-cookbooks/backup/recipes/default.rb index 482753f..f02f585 100644 --- a/site-cookbooks/backup/recipes/default.rb +++ b/site-cookbooks/backup/recipes/default.rb @@ -24,7 +24,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gem_package 'backup' do - version '4.2.3' + version '4.4.0' end backup_data = Chef::EncryptedDataBagItem.load('credentials', 'backup') diff --git a/site-cookbooks/backup/templates/default/config.rb.erb b/site-cookbooks/backup/templates/default/config.rb.erb index 6887e11..736aa33 100644 --- a/site-cookbooks/backup/templates/default/config.rb.erb +++ b/site-cookbooks/backup/templates/default/config.rb.erb @@ -45,6 +45,15 @@ Database::MySQL.defaults do |db| end <%- end -%> +<%- if node["backup"]["postgresql"] -%> +Database::MySQL.defaults do |db| + db.host = "<%= node["backup"]["postgresql"]["host"] %>" + db.username = "<%= node["backup"]["postgresql"]["username"] %>" + db.password = "<%= node["backup"]["postgresql"]["password"] %>" + db.additional_options = ['--quick', '--single-transaction'] +end +<%- end -%> + Database::Redis.defaults do |db| db.host = "<%= node["backup"]["redis"]["host"] %>" db.port = 6379 @@ -53,6 +62,20 @@ Database::Redis.defaults do |db| <%# db.socket = "/tmp/redis.sock"%> end +<%- if node["backup"]["postgresql"] -%> + database PostgreSQL do |db| + db.username = "" + db.password = "<%= node['postgresql']['password']['postgres'] %>" + db.host = "localhost" + db.port = 5432 + db.socket = "/tmp/pg.sock" + # When dumping all databases, `skip_tables` and `only_tables` are ignored. + db.skip_tables = ['skip', 'these', 'tables'] + db.only_tables = ['only', 'these' 'tables'] + db.additional_options = [] + end +<% end -%> + preconfigure 'KosmosBackup' do split_into_chunks_of 250 # megabytes store_with S3 diff --git a/site-cookbooks/kosmos-base/recipes/firewall.rb b/site-cookbooks/kosmos-base/recipes/firewall.rb index bbabe75..2aff21f 100644 --- a/site-cookbooks/kosmos-base/recipes/firewall.rb +++ b/site-cookbooks/kosmos-base/recipes/firewall.rb @@ -21,3 +21,21 @@ firewall_rule 'mosh' do protocol :udp command :allow end + +firewall_rule 'hubot_express_hal8000' do + port 8080 + protocol :tcp + command :allow +end + +firewall_rule 'hubot_express_botka_xmpp' do + port 8082 + protocol :tcp + command :allow +end + +firewall_rule 'hubot_express_schlupp_xmpp' do + port 8083 + protocol :tcp + command :allow +end diff --git a/site-cookbooks/kosmos-base/recipes/letsencrypt.rb b/site-cookbooks/kosmos-base/recipes/letsencrypt.rb index ad435e2..716c3a2 100644 --- a/site-cookbooks/kosmos-base/recipes/letsencrypt.rb +++ b/site-cookbooks/kosmos-base/recipes/letsencrypt.rb @@ -10,7 +10,7 @@ git "/usr/local/certbot" do repository "https://github.com/certbot/certbot" action :sync - revision "v0.12.0" + revision "v0.13.0" user "root" group "root" end diff --git a/site-cookbooks/kosmos-hubot/metadata.rb b/site-cookbooks/kosmos-hubot/metadata.rb index e3406c5..a41559b 100644 --- a/site-cookbooks/kosmos-hubot/metadata.rb +++ b/site-cookbooks/kosmos-hubot/metadata.rb @@ -7,5 +7,6 @@ long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) version '0.1.0' depends 'kosmos-nodejs' -depends 'application' -depends 'application_nodejs' +depends 'kosmos-redis' +depends 'firewall' +depends 'application_javascript' diff --git a/site-cookbooks/kosmos-hubot/recipes/default.rb b/site-cookbooks/kosmos-hubot/recipes/default.rb index 8b44001..e0f3fe6 100644 --- a/site-cookbooks/kosmos-hubot/recipes/default.rb +++ b/site-cookbooks/kosmos-hubot/recipes/default.rb @@ -2,15 +2,18 @@ # Cookbook Name:: kosmos-hubot # Recipe:: default # -# Copyright 2015, Kosmos +# Copyright 2017, Kosmos # # All rights reserved - Do Not Redistribute # -firewall_rule 'hubot_express_hal8000' do - port 8080 - protocol :tcp - command :allow +unless node.chef_environment == "development" + include_recipe 'firewall' + firewall_rule 'hubot_express_hal8000' do + port 8080 + protocol :tcp + command :allow + end end include_recipe "kosmos-nodejs" @@ -25,113 +28,153 @@ user "hubot" do uid 48268 gid 48268 shell "/bin/bash" - home "/srv/hal8000" end hal8000_freenode_data_bag_item = Chef::EncryptedDataBagItem.load('credentials', 'hal8000_freenode') -application "hal8000" do - path "/srv/hal8000" +hal8000_path = "/opt/hal8000" +application hal8000_path do owner "hubot" group "hubot" - action :deploy - - before_restart do - file "#{new_resource.release_path}/external-scripts.json" do - mode "0640" - owner "hubot" - group "hubot" - content [ - "hubot-help", - "hubot-read-tweet", - "hubot-redis-brain", - "hubot-rules", - "hubot-shipit", - "hubot-plusplus", - "hubot-tell", - "hubot-seen", - "hubot-rss-reader", - "hubot-incoming-webhook", - "hubot-auth" - ].to_json - end + git do + repository "https://github.com/67P/hal8000.git" + revision "master" end - repository "https://github.com/67P/hal8000.git" - revision "master" + file "#{name}/external-scripts.json" do + mode "0640" + owner "hubot" + group "hubot" + content [ + "hubot-help", + "hubot-read-tweet", + "hubot-redis-brain", + "hubot-rules", + "hubot-shipit", + "hubot-plusplus", + "hubot-tell", + "hubot-seen", + "hubot-rss-reader", + "hubot-incoming-webhook", + "hubot-auth" + ].to_json + end - nodejs do - entry_point "/srv/hal8000/current/bin/hubot -a irc" - # Use our own systemd service that depends on redis-server - template "nodejs.systemd.service.erb" - environment "HUBOT_IRC_SERVER" => "irc.freenode.net", - "HUBOT_IRC_ROOMS" => "#5apps,#kosmos,#kosmos-dev,#remotestorage,#hackerbeach,#unhosted,#sockethub", - "HUBOT_IRC_NICK" => "hal8000", - "HUBOT_IRC_NICKSERV_USERNAME" => "hal8000", - "HUBOT_IRC_NICKSERV_PASSWORD" => hal8000_freenode_data_bag_item['nickserv_password'], - "HUBOT_IRC_UNFLOOD" => "100", - "HUBOT_RSS_PRINTSUMMARY" => "false", - "HUBOT_RSS_IRCCOLORS" => "true", - # "HUBOT_LOG_LEVEL" => "error", - "EXPRESS_PORT" => "8080", - "HUBOT_RSS_HEADER" => "Update:", - "HUBOT_AUTH_ADMIN" => "bkero,derbumi,galfert,gregkare,jaaan,slvrbckt,raucao", - "OA_ASSET_FROM_ADDRESS" => "akRWZJMETdM2U5UGKadKhv1PAj2npoGja1m", - "OA_DEFAULT_QUANTITY" => "100", - "OA_ASSET_ID" => "AbDn6L2AUGnDreUuNkGFEqcxnsoUP4HCjm", - "OA_SERVER_URL" => "http://localhost:4562", - "OA_SERVER_USERNAME" => "kosmos", - "OA_SERVER_PASSWORD" => "asEjdak1yqw", - "OA_MAX_QUANTITY" => "5000", - "OA_BOT_KEYWORD" => "kredits", - "OA_PLUSPLUS_ROOMS" => "#kosmos", - "WEBHOOK_TOKEN" => hal8000_freenode_data_bag_item['webhook_token'] + npm_install do + user "hubot" + end + + execute "systemctl daemon-reload" do + command "systemctl daemon-reload" + action :nothing + end + + template "/lib/systemd/system/hal8000_nodejs.service" do + source 'nodejs.systemd.service.erb' + owner 'root' + group 'root' + mode '0644' + variables( + :user => "hubot", + :group => "hubot", + :app_dir => hal8000_path, + :entry => "#{hal8000_path}/bin/hubot -a irc", + :environment => { "HUBOT_IRC_SERVER" => "irc.freenode.net", + "HUBOT_IRC_ROOMS" => "#5apps,#kosmos,#kosmos-dev,#remotestorage,#hackerbeach,#unhosted,#sockethub", + "HUBOT_IRC_NICK" => "hal8000", + "HUBOT_IRC_NICKSERV_USERNAME" => "hal8000", + "HUBOT_IRC_NICKSERV_PASSWORD" => hal8000_freenode_data_bag_item['nickserv_password'], + "HUBOT_IRC_UNFLOOD" => "100", + "HUBOT_RSS_PRINTSUMMARY" => "false", + "HUBOT_RSS_IRCCOLORS" => "true", + # "HUBOT_LOG_LEVEL" => "error", + "EXPRESS_PORT" => "8080", + "HUBOT_RSS_HEADER" => "Update:", + "HUBOT_AUTH_ADMIN" => "bkero,derbumi,galfert,gregkare,jaaan,slvrbckt,raucao", + "OA_ASSET_FROM_ADDRESS" => "akRWZJMETdM2U5UGKadKhv1PAj2npoGja1m", + "OA_DEFAULT_QUANTITY" => "100", + "OA_ASSET_ID" => "AbDn6L2AUGnDreUuNkGFEqcxnsoUP4HCjm", + "OA_SERVER_URL" => "http://localhost:4562", + "OA_SERVER_USERNAME" => "kosmos", + "OA_SERVER_PASSWORD" => "asEjdak1yqw", + "OA_MAX_QUANTITY" => "5000", + "OA_BOT_KEYWORD" => "kredits", + "OA_PLUSPLUS_ROOMS" => "#kosmos", + "WEBHOOK_TOKEN" => hal8000_freenode_data_bag_item['webhook_token'] } + ) + notifies :run, "execute[systemctl daemon-reload]", :delayed + notifies :restart, "service[hal8000_nodejs]", :delayed + end + + service "hal8000_nodejs" do + action [:enable, :start] end end botka_freenode_data_bag_item = Chef::EncryptedDataBagItem.load('credentials', 'botka_freenode') -application "botka_freenode" do - path "/srv/botka_freenode" +botka_freenode_path = "/opt/botka_freenode" +application botka_freenode_path do owner "hubot" group "hubot" - action :deploy - - before_restart do - file "#{new_resource.release_path}/external-scripts.json" do - mode "0640" - owner "hubot" - group "hubot" - content [ - "hubot-help", - "hubot-remotestorage-logger" - ].to_json - end + git do + repository "https://github.com/67P/botka.git" + revision "master" end - repository "https://github.com/67P/botka.git" - revision "master" + file "#{name}/external-scripts.json" do + mode "0640" + owner "hubot" + group "hubot" + content [ + "hubot-help", + "hubot-remotestorage-logger" + ].to_json + end - nodejs do - entry_point "/srv/botka_freenode/current/bin/hubot -a irc" - # Use our own systemd service that depends on redis-server - template "nodejs.systemd.service.erb" - environment "HUBOT_IRC_SERVER" => "irc.freenode.net", - "HUBOT_IRC_ROOMS" => "#5apps,#kosmos,#kosmos-dev,#remotestorage,#hackerbeach,#unhosted,#sockethub,#opensourcedesign,#openknot,#emberjs", - "HUBOT_IRC_NICK" => "botka", - "HUBOT_IRC_NICKSERV_USERNAME" => "botka", - "HUBOT_IRC_NICKSERV_PASSWORD" => botka_freenode_data_bag_item['nickserv_password'], - "HUBOT_IRC_UNFLOOD" => "100", - "HUBOT_RSS_PRINTSUMMARY" => "false", - "HUBOT_RSS_IRCCOLORS" => "true", - # "HUBOT_LOG_LEVEL" => "error", - "EXPRESS_PORT" => "8082", - "HUBOT_AUTH_ADMIN" => "bkero,derbumi,galfert,gregkare,jaaan,slvrbckt,raucao", - "RS_LOGGER_USER" => "kosmos@5apps.com", - "RS_LOGGER_TOKEN" => botka_freenode_data_bag_item['rs_logger_token'], - "RS_LOGGER_SERVER_NAME" => "freenode", - "RS_LOGGER_PUBLIC" => "true" + npm_install do + user "hubot" + end + + + execute "systemctl daemon-reload" do + command "systemctl daemon-reload" + action :nothing + end + + template "/lib/systemd/system/botka_freenode_nodejs.service" do + source 'nodejs.systemd.service.erb' + owner 'root' + group 'root' + mode '0644' + variables( + :user => "hubot", + :group => "hubot", + :app_dir => botka_freenode_path, + :entry => "#{botka_freenode_path}/bin/hubot -a irc", + :environment => { "HUBOT_IRC_SERVER" => "irc.freenode.net", + "HUBOT_IRC_ROOMS" => "#5apps,#kosmos,#kosmos-dev,#remotestorage,#hackerbeach,#unhosted,#sockethub,#opensourcedesign,#openknot,#emberjs,#mastodon,#indieweb", + "HUBOT_IRC_NICK" => "botka", + "HUBOT_IRC_NICKSERV_USERNAME" => "botka", + "HUBOT_IRC_NICKSERV_PASSWORD" => botka_freenode_data_bag_item['nickserv_password'], + "HUBOT_IRC_UNFLOOD" => "100", + "HUBOT_RSS_PRINTSUMMARY" => "false", + "HUBOT_RSS_IRCCOLORS" => "true", + # "HUBOT_LOG_LEVEL" => "error", + "EXPRESS_PORT" => "8082", + "HUBOT_AUTH_ADMIN" => "bkero,derbumi,galfert,gregkare,jaaan,slvrbckt,raucao", + "RS_LOGGER_USER" => "kosmos@5apps.com", + "RS_LOGGER_TOKEN" => botka_freenode_data_bag_item['rs_logger_token'], + "RS_LOGGER_SERVER_NAME" => "freenode", + "RS_LOGGER_PUBLIC" => "true" } + ) + notifies :run, "execute[systemctl daemon-reload]", :delayed + notifies :restart, "service[botka_freenode_nodejs]", :delayed + end + + service "botka_freenode_nodejs" do + action [:enable, :start] end end diff --git a/site-cookbooks/kosmos-ipfs/recipes/letsencrypt.rb b/site-cookbooks/kosmos-ipfs/recipes/letsencrypt.rb index 2775c60..d33cb9e 100644 --- a/site-cookbooks/kosmos-ipfs/recipes/letsencrypt.rb +++ b/site-cookbooks/kosmos-ipfs/recipes/letsencrypt.rb @@ -8,7 +8,9 @@ # # nginx config to generate a Let's Encrypt cert -include_recipe "kosmos-base::letsencrypt" +unless node.chef_environment == "development" + include_recipe "kosmos-base::letsencrypt" +end root_directory = "/var/www/ipfs.kosmos.org" @@ -37,20 +39,23 @@ nginx_site 'ipfs.kosmos.org' do enable true end -firewall_rule 'ipfs_api' do - port 5444 - protocol :tcp - command :allow -end - -# Generate a Let's Encrypt cert (only if the nginx vhost exists and no cert -# has been generated before. The renew cron will take care of renewing -execute "letsencrypt cert for ipfs.kosmos.org" do - command "./certbot-auto certonly --webroot --agree-tos --email ops@5apps.com --webroot-path #{root_directory} -d ipfs.kosmos.org -n" - cwd "/usr/local/certbot" - only_if do - File.exist?("#{node['nginx']['dir']}/sites-enabled/ipfs.kosmos.org") && - ! File.exist?("/etc/letsencrypt/live/ipfs.kosmos.org/fullchain.pem") +unless node.chef_environment == "development" + include_recipe "firewall" + firewall_rule 'ipfs_api' do + port 5444 + protocol :tcp + command :allow + end + + # Generate a Let's Encrypt cert (only if the nginx vhost exists and no cert + # has been generated before. The renew cron will take care of renewing + execute "letsencrypt cert for ipfs.kosmos.org" do + command "./certbot-auto certonly --webroot --agree-tos --email ops@5apps.com --webroot-path #{root_directory} -d ipfs.kosmos.org -n" + cwd "/usr/local/certbot" + only_if do + File.exist?("#{node['nginx']['dir']}/sites-enabled/ipfs.kosmos.org") && + ! File.exist?("/etc/letsencrypt/live/ipfs.kosmos.org/fullchain.pem") + end + notifies :create, "template[#{node['nginx']['dir']}/sites-available/ipfs.kosmos.org]", :delayed end - notifies :create, "template[#{node['nginx']['dir']}/sites-available/ipfs.kosmos.org]", :delayed end diff --git a/site-cookbooks/kosmos-mastodon/CHANGELOG.md b/site-cookbooks/kosmos-mastodon/CHANGELOG.md new file mode 100644 index 0000000..4e30fed --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/CHANGELOG.md @@ -0,0 +1,11 @@ +# kosmos-mastodon CHANGELOG + +This file is used to list changes made in each version of the kosmos-mastodon cookbook. + +## 0.1.0 +- [your_name] - Initial release of kosmos-mastodon + +- - - +Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown. + +The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown. diff --git a/site-cookbooks/kosmos-mastodon/README.md b/site-cookbooks/kosmos-mastodon/README.md new file mode 100644 index 0000000..7f3e489 --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/README.md @@ -0,0 +1,80 @@ +# kosmos-mastodon Cookbook + +TODO: Enter the cookbook description here. + +e.g. +This cookbook makes your favorite breakfast sandwich. + +## Requirements + +TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc. + +e.g. +### Platforms + +- SandwichOS + +### Chef + +- Chef 12.0 or later + +### Cookbooks + +- `toaster` - kosmos-mastodon needs toaster to brown your bagel. + +## Attributes + +TODO: List your cookbook attributes here. + +e.g. +### kosmos-mastodon::default + + + + + + + + + + + + + + +
KeyTypeDescriptionDefault
['kosmos-mastodon']['bacon']Booleanwhether to include bacontrue
+ +## Usage + +### kosmos-mastodon::default + +TODO: Write usage instructions for each cookbook. + +e.g. +Just include `kosmos-mastodon` in your node's `run_list`: + +```json +{ + "name":"my_node", + "run_list": [ + "recipe[kosmos-mastodon]" + ] +} +``` + +## Contributing + +TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section. + +e.g. +1. Fork the repository on Github +2. Create a named feature branch (like `add_component_x`) +3. Write your change +4. Write tests for your change (if applicable) +5. Run the tests, ensuring they all pass +6. Submit a Pull Request using Github + +## License and Authors + +Authors: TODO: List authors + diff --git a/site-cookbooks/kosmos-mastodon/attributes/default.rb b/site-cookbooks/kosmos-mastodon/attributes/default.rb new file mode 100644 index 0000000..801cd7e --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/attributes/default.rb @@ -0,0 +1,4 @@ +node.default["kosmos-mastodon"]["directory"] = "/opt/mastodon" +node.default["kosmos-mastodon"]["puma_port"] = 3000 +node.default["kosmos-mastodon"]["streaming_port"] = 4000 +node.default["kosmos-mastodon"]["server_name"] = "kosmos.social" diff --git a/site-cookbooks/kosmos-mastodon/metadata.rb b/site-cookbooks/kosmos-mastodon/metadata.rb new file mode 100644 index 0000000..6447a64 --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/metadata.rb @@ -0,0 +1,15 @@ +name 'kosmos-mastodon' +maintainer 'Kosmos' +maintainer_email 'mail@kosmos.org' +license 'All rights reserved' +description 'Installs/Configures kosmos-mastodon' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '0.1.0' + +depends "kosmos-nginx" +depends "kosmos-nodejs" +depends "kosmos-ruby" +depends "application_ruby" +depends "application_javascript" +depends "postgresql" +depends "database" diff --git a/site-cookbooks/kosmos-mastodon/recipes/default.rb b/site-cookbooks/kosmos-mastodon/recipes/default.rb new file mode 100644 index 0000000..694ca15 --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/recipes/default.rb @@ -0,0 +1,156 @@ +# +# Cookbook Name:: kosmos-mastodon +# Recipe:: default +# +# Copyright 2017, Kosmos +# +# All rights reserved - Do Not Redistribute +# + +include_recipe "kosmos-nodejs" +include_recipe "kosmos-ruby" +node.override['postgresql']['enable_pgdg_apt'] = false +include_recipe "postgresql::server" +include_recipe "postgresql::ruby" +unless node.chef_environment == "development" + node.override['postgresql']['config_pgtune']['db_type'] = "web" + include_recipe "postgresql::config_pgtune" +end + +postgresql_database 'mastodon' do + connection( + :host => '127.0.0.1', + :port => 5432, + :username => 'postgres', + :password => node['postgresql']['password']['postgres'] + ) + action :create +end + +mastodon_path = node["kosmos-mastodon"]["directory"] + +group "mastodon" do + gid 62786 +end + +user "mastodon" do + comment "mastodon user" + uid 62786 + gid 62786 + shell "/bin/bash" + home mastodon_path +end + +package %w(imagemagick ffmpeg libxml2-dev libxslt1-dev file git curl) +node_package %w(yarn) + +application mastodon_path do + owner "mastodon" + group "mastodon" + + git do + user "mastodon" + group "mastodon" + repository "https://github.com/67P/mastodon.git" + revision "redis_db" + end + + mastodon_credentials = Chef::EncryptedDataBagItem.load('credentials', 'mastodon') + + template ".env.production" do + source "env.production.erb" + mode "0640" + owner "mastodon" + group "mastodon" + variables redis_db: 1, + redis_actioncable_db: 2, + domain: "kosmos.social", + paperclip_secret: mastodon_credentials['paperclip_secret'], + secret_key_base: mastodon_credentials['secret_key_base'], + otp_secret: mastodon_credentials['otp_secret'], + smtp_login: mastodon_credentials['smtp_user_name'], + smtp_password: mastodon_credentials['smtp_password'], + smtp_from_address: "mail@kosmos.social", + s3_bucket: "kosmos-social", + aws_access_key_id: mastodon_credentials['aws_access_key_id'], + aws_secret_access_key: mastodon_credentials['aws_secret_access_key'], + s3_region: "eu-west-1" + end + + directory "#{mastodon_path}/public/.well-known" do + owner node['nginx']['user'] + group node['nginx']['group'] + recursive true + end + + bundle_install do + user "mastodon" + deployment true + without %w{development test} + end + + npm_install do + user "mastodon" + end + + rails do + migrate true + rails_env "production" + end + + execute "systemctl daemon-reload" do + command "systemctl daemon-reload" + action :nothing + end + + # mastodon-web service + # + template "/lib/systemd/system/mastodon-web.service" do + source "mastodon-web.systemd.service.erb" + variables user: user, + app_dir: mastodon_path, + port: node["kosmos-mastodon"]["puma_port"] + notifies :run, "execute[systemctl daemon-reload]", :delayed + notifies :restart, "service[mastodon-web]", :delayed + end + + service "mastodon-web" do + action [:enable, :start] + end + + # mastodon-sidekiq service + # + template "/lib/systemd/system/mastodon-sidekiq.service" do + source "mastodon-sidekiq.systemd.service.erb" + variables user: user, + app_dir: mastodon_path + notifies :run, "execute[systemctl daemon-reload]", :delayed + notifies :restart, "service[mastodon-sidekiq]", :delayed + end + + service "mastodon-sidekiq" do + action [:enable, :start] + end + + # mastodon-streaming service + # + template "/lib/systemd/system/mastodon-streaming.service" do + source "mastodon-streaming.systemd.service.erb" + variables user: user, + app_dir: mastodon_path, + port: node["kosmos-mastodon"]["streaming_port"] + notifies :run, "execute[systemctl daemon-reload]", :delayed + notifies :restart, "service[mastodon-streaming]", :delayed + end + + service "mastodon-streaming" do + action [:enable, :start] + end +end + +# unless node.chef_environment == "development" +# node.override["backup"]["postgresql"]["host"] = "localhost" +# node.override["backup"]["postgresql"]["username"] = "postgres" +# node.override["backup"]["postgresql"]["password"] = node['postgresql']['password']['postgres'] +# include_recipe "backup" +# end diff --git a/site-cookbooks/kosmos-mastodon/recipes/nginx.rb b/site-cookbooks/kosmos-mastodon/recipes/nginx.rb new file mode 100644 index 0000000..8f314a6 --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/recipes/nginx.rb @@ -0,0 +1,48 @@ +# +# Cookbook Name:: kosmos-mastodon +# Recipe:: nginx +# +# Copyright 2017, Kosmos +# +# All rights reserved - Do Not Redistribute +# + +mastodon_path = node["kosmos-mastodon"]["directory"] +server_name = node["kosmos-mastodon"]["server_name"] + +include_recipe "kosmos-nginx" + +directory "/var/www/mastodon/.well-known/acme-challenge" do + owner node["nginx"]["user"] + group node["nginx"]["group"] + recursive true + action :create +end + +template "#{node['nginx']['dir']}/sites-available/mastodon" do + source 'nginx_conf_mastodon.erb' + owner 'www-data' + mode 0640 + variables streaming_port: node["kosmos-mastodon"]["streaming_port"], + puma_port: node["kosmos-mastodon"]["puma_port"], + server_name: server_name, + ssl_cert: "/etc/letsencrypt/live/#{server_name}/fullchain.pem", + ssl_key: "/etc/letsencrypt/live/#{server_name}/privkey.pem", + mastodon_path: mastodon_path + notifies :reload, 'service[nginx]', :delayed +end + +nginx_site 'mastodon' do + enable true +end + +unless node.chef_environment == "development" + include_recipe "kosmos-base::letsencrypt" + execute "letsencrypt cert for kosmos.social" do + command "./certbot-auto certonly --webroot --agree-tos --email ops@5apps.com --webroot-path /var/www/mastodon -d #{server_name} -n" + cwd "/usr/local/certbot" + not_if { File.exist? "/etc/letsencrypt/live/#{server_name}/fullchain.pem" } + notifies :reload, "service[nginx]", :delayed + notifies :create, "template[#{node['nginx']['dir']}/sites-available/mastodon]", :immediately + end +end diff --git a/site-cookbooks/kosmos-mastodon/templates/default/config-environment-production.rb.erb b/site-cookbooks/kosmos-mastodon/templates/default/config-environment-production.rb.erb new file mode 100644 index 0000000..d4ea1e0 --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/templates/default/config-environment-production.rb.erb @@ -0,0 +1,119 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + config.action_controller.asset_host = ENV['CDN_HOST'] if ENV.key?('CDN_HOST') + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + # Compress JavaScripts and CSS. + config.assets.js_compressor = Uglifier.new(mangle: false) + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Allow to specify public IP of reverse proxy if it's needed + config.action_dispatch.trusted_proxies = [IPAddr.new(ENV['TRUSTED_PROXY_IP'])] unless ENV['TRUSTED_PROXY_IP'].blank? + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + config.force_ssl = false + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [:request_id] + + # Use a different logger for distributed setups. + # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + + # Parse and split the REDIS_URL if passed (used with hosting platforms such as Heroku). + # Set ENV variables because they are used elsewhere. + if ENV['REDIS_URL'] + redis_url = URI.parse(ENV['REDIS_URL']) + ENV['REDIS_HOST'] = redis_url.host + ENV['REDIS_PORT'] = redis_url.port.to_s + ENV['REDIS_PASSWORD'] = redis_url.password + end + + # Use a different cache store in production. + config.cache_store = :redis_store, { + host: ENV.fetch('REDIS_HOST') { 'localhost' }, + port: ENV.fetch('REDIS_PORT') { 6379 }, + password: ENV.fetch('REDIS_PASSWORD') { false }, + db: <%= @redis_db %>, + namespace: 'cache', + expires_in: 20.minutes, + } + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Better log formatting + config.lograge.enabled = true + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false + + config.action_mailer.perform_caching = false + + # E-mails + config.action_mailer.smtp_settings = { + :port => ENV['SMTP_PORT'], + :address => ENV['SMTP_SERVER'], + :user_name => ENV['SMTP_LOGIN'], + :password => ENV['SMTP_PASSWORD'], + :domain => ENV['SMTP_DOMAIN'] || config.x.local_domain, + :authentication => :plain, + } + + config.action_mailer.delivery_method = :smtp + + config.react.variant = :production + + config.to_prepare do + StatsD.backend = StatsD::Instrument::Backends::NullBackend.new if ENV['STATSD_ADDR'].blank? + end + + config.action_dispatch.default_headers = { + 'Server' => 'Mastodon', + 'X-Frame-Options' => 'DENY', + 'X-Content-Type-Options' => 'nosniff', + 'X-XSS-Protection' => '1; mode=block', + } +end diff --git a/site-cookbooks/kosmos-mastodon/templates/default/env.production.erb b/site-cookbooks/kosmos-mastodon/templates/default/env.production.erb new file mode 100644 index 0000000..9aa0da0 --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/templates/default/env.production.erb @@ -0,0 +1,49 @@ +# Service dependencies +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_DB=<%= @redis_db %> +REDIS_ACTIONCABLE_DB=<%= @redis_actioncable_db %> +DB_HOST=localhost +DB_USER=postgres +DB_NAME=mastodon +DB_PASS=<%= node['postgresql']['password']['postgres'] %> +DB_PORT=5432 + +# Federation +LOCAL_DOMAIN=<%= @domain %> +LOCAL_HTTPS=true + +# Application secrets +# Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose) +PAPERCLIP_SECRET=<%= @paperclip_secret %> +SECRET_KEY_BASE=<%= @secret_key_base %> +OTP_SECRET=<%= @otp_secret %> + +# Registrations +# Single user mode will disable registrations and redirect frontpage to the first profile +# SINGLE_USER_MODE=true +# Prevent registrations with following e-mail domains +# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc + +# E-mail configuration +SMTP_SERVER=smtp.mailgun.org +SMTP_PORT=587 +SMTP_LOGIN=<%= @smtp_login %> +SMTP_PASSWORD=<%= @smtp_password %> +SMTP_FROM_ADDRESS=<%= @smtp_from_address %> + +# Optional asset host for multi-server setups +# CDN_HOST=assets.example.com + +# S3 (optional) +S3_ENABLED=true +S3_BUCKET=<%= @s3_bucket %> +AWS_ACCESS_KEY_ID=<%= @aws_access_key_id %> +AWS_SECRET_ACCESS_KEY=<%= @aws_secret_access_key %> +S3_REGION=<%= @s3_region %> + +# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front +# S3_CLOUDFRONT_HOST= + +# Streaming API integration +# STREAMING_API_BASE_URL= diff --git a/site-cookbooks/kosmos-mastodon/templates/default/mastodon-sidekiq.systemd.service.erb b/site-cookbooks/kosmos-mastodon/templates/default/mastodon-sidekiq.systemd.service.erb new file mode 100644 index 0000000..86a3759 --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/templates/default/mastodon-sidekiq.systemd.service.erb @@ -0,0 +1,17 @@ +[Unit] +Description=mastodon-sidekiq +Requires=redis-server.service +After=redis-server.service + +[Service] +Type=simple +User=<%= @user %> +WorkingDirectory=<%= @app_dir %> +Environment="RAILS_ENV=production" +Environment="DB_POOL=5" +ExecStart=/usr/local/bin/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push +TimeoutSec=15 +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/site-cookbooks/kosmos-mastodon/templates/default/mastodon-streaming.systemd.service.erb b/site-cookbooks/kosmos-mastodon/templates/default/mastodon-streaming.systemd.service.erb new file mode 100644 index 0000000..7756124 --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/templates/default/mastodon-streaming.systemd.service.erb @@ -0,0 +1,15 @@ +Description=mastodon-streaming +After=network.target + +[Service] +Type=simple +User=<%= @user %> +WorkingDirectory=<%= @app_dir %> +Environment="NODE_ENV=production" +Environment="PORT=<%= @port %>" +ExecStart=/usr/local/bin/npm run start +TimeoutSec=15 +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/site-cookbooks/kosmos-mastodon/templates/default/mastodon-web.systemd.service.erb b/site-cookbooks/kosmos-mastodon/templates/default/mastodon-web.systemd.service.erb new file mode 100644 index 0000000..879d87c --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/templates/default/mastodon-web.systemd.service.erb @@ -0,0 +1,19 @@ +[Unit] +Description=mastodon-web +Requires=redis-server.service +After=redis-server.service +Requires=postgresql@9.4-main.service +After=postgresql@9.4-main.service + +[Service] +Type=simple +User=<%= @user %> +WorkingDirectory=<%= @app_dir %> +Environment="RAILS_ENV=production" +Environment="PORT=3000" +ExecStart=/usr/local/bin/bundle exec puma -C config/puma.rb +TimeoutSec=15 +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/site-cookbooks/kosmos-mastodon/templates/default/nginx_conf_mastodon.erb b/site-cookbooks/kosmos-mastodon/templates/default/nginx_conf_mastodon.erb new file mode 100644 index 0000000..2f87598 --- /dev/null +++ b/site-cookbooks/kosmos-mastodon/templates/default/nginx_conf_mastodon.erb @@ -0,0 +1,88 @@ +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + listen 80; + server_name <%= @server_name %>; + + access_log "/var/log/nginx/mastodon.access.log"; + error_log "/var/log/nginx/mastodon.error.log"; + + location /.well-known { + root "/var/www/mastodon"; + } + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name <%= @server_name %>; + + access_log "/var/log/nginx/mastodon.access.log"; + error_log "/var/log/nginx/mastodon.error.log"; + + ssl_protocols TLSv1.2; + ssl_ciphers EECDH+AESGCM:EECDH+AES; + ssl_ecdh_curve secp384r1; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + + <% if File.exist?(@ssl_cert) && + File.exist?(@ssl_key) -%> + ssl_certificate <%= @ssl_cert %>; + ssl_certificate_key <%= @ssl_key %>; + <% end -%> + + keepalive_timeout 70; + sendfile on; + client_max_body_size 0; + gzip off; + + root <%= @mastodon_path %>/public; + + # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; + + location / { + try_files $uri @proxy; + } + + location @proxy { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + + proxy_pass_header Server; + + proxy_pass http://localhost:<%= @puma_port %>; + proxy_buffering off; + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + tcp_nodelay on; + } + + location /api/v1/streaming { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + + proxy_pass http://localhost:<%= @streaming_port %>; + proxy_buffering off; + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + tcp_nodelay on; + } + + error_page 500 501 502 503 504 /500.html; +} diff --git a/site-cookbooks/kosmos-mediawiki/metadata.rb b/site-cookbooks/kosmos-mediawiki/metadata.rb index c25037b..0df6188 100644 --- a/site-cookbooks/kosmos-mediawiki/metadata.rb +++ b/site-cookbooks/kosmos-mediawiki/metadata.rb @@ -7,6 +7,5 @@ long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) version '0.1.0' depends "mediawiki" -depends "firewall" depends "ark" depends "backup" diff --git a/site-cookbooks/kosmos-nginx/recipes/default.rb b/site-cookbooks/kosmos-nginx/recipes/default.rb index e6e2042..6a17fc2 100644 --- a/site-cookbooks/kosmos-nginx/recipes/default.rb +++ b/site-cookbooks/kosmos-nginx/recipes/default.rb @@ -24,10 +24,13 @@ EOF include_recipe 'nginx' -include_recipe 'kosmos-base::firewall' -firewall_rule 'http/https' do - port [80, 443] - protocol :tcp - command :allow +unless node.chef_environment == "development" + include_recipe 'kosmos-base::firewall' + + firewall_rule 'http/https' do + port [80, 443] + protocol :tcp + command :allow + end end diff --git a/site-cookbooks/kosmos-ruby/CHANGELOG.md b/site-cookbooks/kosmos-ruby/CHANGELOG.md new file mode 100644 index 0000000..e0c9479 --- /dev/null +++ b/site-cookbooks/kosmos-ruby/CHANGELOG.md @@ -0,0 +1,11 @@ +# kosmos-ruby CHANGELOG + +This file is used to list changes made in each version of the kosmos-ruby cookbook. + +## 0.1.0 +- [your_name] - Initial release of kosmos-ruby + +- - - +Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown. + +The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown. diff --git a/site-cookbooks/kosmos-ruby/README.md b/site-cookbooks/kosmos-ruby/README.md new file mode 100644 index 0000000..2daaba1 --- /dev/null +++ b/site-cookbooks/kosmos-ruby/README.md @@ -0,0 +1,80 @@ +# kosmos-ruby Cookbook + +TODO: Enter the cookbook description here. + +e.g. +This cookbook makes your favorite breakfast sandwich. + +## Requirements + +TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc. + +e.g. +### Platforms + +- SandwichOS + +### Chef + +- Chef 12.0 or later + +### Cookbooks + +- `toaster` - kosmos-ruby needs toaster to brown your bagel. + +## Attributes + +TODO: List your cookbook attributes here. + +e.g. +### kosmos-ruby::default + + + + + + + + + + + + + + +
KeyTypeDescriptionDefault
['kosmos-ruby']['bacon']Booleanwhether to include bacontrue
+ +## Usage + +### kosmos-ruby::default + +TODO: Write usage instructions for each cookbook. + +e.g. +Just include `kosmos-ruby` in your node's `run_list`: + +```json +{ + "name":"my_node", + "run_list": [ + "recipe[kosmos-ruby]" + ] +} +``` + +## Contributing + +TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section. + +e.g. +1. Fork the repository on Github +2. Create a named feature branch (like `add_component_x`) +3. Write your change +4. Write tests for your change (if applicable) +5. Run the tests, ensuring they all pass +6. Submit a Pull Request using Github + +## License and Authors + +Authors: TODO: List authors + diff --git a/site-cookbooks/kosmos-ruby/attributes/default.rb b/site-cookbooks/kosmos-ruby/attributes/default.rb new file mode 100644 index 0000000..2f7703f --- /dev/null +++ b/site-cookbooks/kosmos-ruby/attributes/default.rb @@ -0,0 +1 @@ +default['kosmos-ruby']['version'] = '2.3' diff --git a/site-cookbooks/kosmos-ruby/metadata.rb b/site-cookbooks/kosmos-ruby/metadata.rb new file mode 100644 index 0000000..2459b56 --- /dev/null +++ b/site-cookbooks/kosmos-ruby/metadata.rb @@ -0,0 +1,7 @@ +name 'kosmos-ruby' +maintainer 'Kosmos' +maintainer_email 'mail@kosmos.org' +license 'All rights reserved' +description 'Installs/Configures kosmos-ruby' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '0.1.0' diff --git a/site-cookbooks/kosmos-ruby/recipes/default.rb b/site-cookbooks/kosmos-ruby/recipes/default.rb new file mode 100644 index 0000000..96a1c9a --- /dev/null +++ b/site-cookbooks/kosmos-ruby/recipes/default.rb @@ -0,0 +1,54 @@ +# +# Cookbook Name:: kosmos-ruby +# Recipe:: default +# +# Copyright 2017, Kosmos +# +# All rights reserved - Do Not Redistribute +# + +package_name = "ruby#{node['kosmos-ruby']['version']}" + +apt_repository 'brightbox_ruby' do + uri 'http://ppa.launchpad.net/brightbox/ruby-ng/ubuntu' + distribution node['lsb']['codename'] + components ['main'] + keyserver 'keyserver.ubuntu.com' + key '80F70E11F0F0D5F10CB20E62F5DA5F09C3173AA6' +end + +packages = [ + "ruby#{node['kosmos-ruby']['version']}", + "ruby#{node['kosmos-ruby']['version']}-dev", + "build-essential", + "libssl-dev", + "zlib1g-dev" +] + +apt_package packages do + action :install +end + +apt_package 'ruby-switch' do + action :install + notifies :run, 'execute[set default ruby]', :immediately +end + +execute 'set default ruby' do + command "ruby-switch --set #{package_name}" + action :nothing + notifies :reload, 'ohai[reload]', :immediately +end + +ohai 'reload' do + action :nothing +end + +execute 'update rubygems' do + command 'gem update --system 2.6.8' + not_if "gem --version | grep ^2.6.8$" +end + +gem_package "bundler" do + version "1.13.2" +end diff --git a/site-cookbooks/sockethub/metadata.rb b/site-cookbooks/sockethub/metadata.rb index f574a74..488e39d 100644 --- a/site-cookbooks/sockethub/metadata.rb +++ b/site-cookbooks/sockethub/metadata.rb @@ -6,8 +6,7 @@ description 'Installs/Configures sockethub' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) version '0.1.0' -depends 'application' -depends 'application_nodejs' +depends 'application_javascript' depends 'kosmos-redis' depends 'kosmos-nodejs' depends 'kosmos-nginx' diff --git a/site-cookbooks/sockethub/recipes/default.rb b/site-cookbooks/sockethub/recipes/default.rb index 1428270..daa6b63 100644 --- a/site-cookbooks/sockethub/recipes/default.rb +++ b/site-cookbooks/sockethub/recipes/default.rb @@ -8,25 +8,45 @@ # include_recipe 'kosmos-nodejs' - include_recipe 'kosmos-redis' +package "git" -application "sockethub" do - path "/srv/sockethub" +path_to_deploy = "/opt/sockethub" +application path_to_deploy do owner "www-data" group "www-data" - action :deploy + git do + repository 'https://github.com/sockethub/sockethub.git' + revision 'v1.0.5' + end - repository 'https://github.com/sockethub/sockethub.git' - revision 'v1.0.5' + npm_install - nodejs do - entry_point '/srv/sockethub/current/bin/sockethub' - # Use our own systemd service that depends on redis-server - template 'nodejs.systemd.service.erb' - environment 'DEBUG' => '*', - 'PORT' => node['sockethub']['port'] + execute "systemctl daemon-reload" do + command "systemctl daemon-reload" + action :nothing + end + + template "/lib/systemd/system/sockethub_nodejs.service" do + source 'nodejs.systemd.service.erb' + owner 'root' + group 'root' + mode '0644' + variables( + :user => owner, + :group => group, + :app_dir => path_to_deploy, + :entry => "/usr/local/bin/node /usr/local/bin/npm start", + :environment => { 'DEBUG' => '*', + 'PORT' => node['sockethub']['port'] } + + ) + notifies :run, "execute[systemctl daemon-reload]", :delayed + notifies :restart, "service[sockethub_nodejs]", :delayed + end + + service "sockethub_nodejs" do + action [:enable, :start] end end - diff --git a/site-cookbooks/sockethub/recipes/proxy.rb b/site-cookbooks/sockethub/recipes/proxy.rb index 0663151..88025da 100644 --- a/site-cookbooks/sockethub/recipes/proxy.rb +++ b/site-cookbooks/sockethub/recipes/proxy.rb @@ -9,10 +9,13 @@ include_recipe "kosmos-base::letsencrypt" -firewall_rule 'sockethub' do - port node['sockethub']['external_port'].to_i - protocol :tcp - command :allow +unless node.chef_environment == "development" + include_recipe "firewall" + firewall_rule 'sockethub' do + port node['sockethub']['external_port'].to_i + protocol :tcp + command :allow + end end include_recipe 'kosmos-nginx' diff --git a/site-cookbooks/sockethub/templates/default/nodejs.systemd.service.erb b/site-cookbooks/sockethub/templates/default/nodejs.systemd.service.erb index 2c42623..8dc98a9 100644 --- a/site-cookbooks/sockethub/templates/default/nodejs.systemd.service.erb +++ b/site-cookbooks/sockethub/templates/default/nodejs.systemd.service.erb @@ -5,6 +5,7 @@ After=redis-server.service [Service] ExecStart=<%= @entry %> +WorkingDirectory=<%= @app_dir %> User=<%= @user %> Group=<%= @group %> <% unless @environment.empty? -%>