diff --git a/Cheffile b/Cheffile index dc8c403..54e0767 100644 --- a/Cheffile +++ b/Cheffile @@ -52,3 +52,11 @@ cookbook "redmine", cookbook "runit", :git => "git@github.com:express42-cookbooks/runit.git" +cookbook "strano", + :path => "inhouse-cookbooks/strano" + +cookbook "chiliproject", + :path => "inhouse-cookbooks/chiliproject" + +cookbook "ssh_known_hosts", + :git => "git@github.com:opscode-cookbooks/ssh_known_hosts.git" diff --git a/Cheffile.lock b/Cheffile.lock index 00fd45f..e39fdb1 100644 --- a/Cheffile.lock +++ b/Cheffile.lock @@ -1,3 +1,12 @@ +SITE + remote: http://community.opscode.com/api/v1 + specs: + build-essential (1.4.2) + partial_search (1.0.2) + rvm (0.0.4) + apt (>= 0) + build-essential (>= 0) + GIT remote: git@github.com:evilmartians/chef-nginx.git ref: master @@ -94,6 +103,14 @@ GIT specs: apt (2.1.1) +GIT + remote: git@github.com:opscode-cookbooks/ssh_known_hosts.git + ref: master + sha: c13eed20b074cd377db0daaca76f4c5b58b12830 + specs: + ssh_known_hosts (1.0.3) + partial_search (>= 0.0.0) + GIT remote: https://github.com/fnichol/chef-user.git ref: master @@ -120,14 +137,28 @@ PATH specs: base (0.1.0) +PATH + remote: inhouse-cookbooks/chiliproject + specs: + chiliproject (0.0.0) + PATH remote: inhouse-cookbooks/fake specs: fake (0.1.0) +PATH + remote: inhouse-cookbooks/strano + specs: + strano (0.0.1) + lvm (>= 0.0.0) + rvm (>= 0.0.0) + sudo (>= 0.0.0) + DEPENDENCIES apt (>= 0) base (>= 0) + chiliproject (>= 0) fake (>= 0) lvm (>= 0) nginx (>= 0) @@ -138,6 +169,8 @@ DEPENDENCIES redmine (>= 0) ruby (>= 0) runit (>= 0) + ssh_known_hosts (>= 0) + strano (>= 0) sudo (>= 0) sysctl (>= 0) timezone (>= 0) diff --git a/Vagrantfile b/Vagrantfile index b6ad5af..1c2ad10 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -3,6 +3,7 @@ Vagrant.configure("2") do |config| main.vm.box = "ubuntu12.04-chef11-chruby" main.vm.hostname = "etalon" config.vm.network :forwarded_port, guest: 8080, host: 7070 + config.vm.network :forwarded_port, guest: 80, host: 7071 main.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", "2048"] end @@ -14,7 +15,8 @@ Vagrant.configure("2") do |config| chef.encrypted_data_bag_secret_key_path = "./.chef/encrypted_data_bag_secret" chef.add_role "base" - chef.add_role "redmine" + chef.add_role "strano" + chef.add_role "chiliproject" end end end diff --git a/data_bags/db/databases.json b/data_bags/db/databases.json index 99bfd5e..d717e5c 100644 --- a/data_bags/db/databases.json +++ b/data_bags/db/databases.json @@ -1,10 +1,20 @@ { "id": "databases", "databases": { + "chiliproject": { + "options": { + "owner": "chiliproject" + } + }, "redmine": { "options": { "owner": "redmine" } + }, + "strano": { + "options": { + "owner": "strano" + } } } } diff --git a/data_bags/db/users.json b/data_bags/db/users.json index 553684d..7066bc4 100644 --- a/data_bags/db/users.json +++ b/data_bags/db/users.json @@ -1,6 +1,8 @@ { "id": "users", "users": { - "redmine": {} + "redmine": {}, + "chiliproject": {}, + "strano": {} } } diff --git a/data_bags/deploy-key/key.json b/data_bags/deploy-key/key.json new file mode 100644 index 0000000..87a8f4d --- /dev/null +++ b/data_bags/deploy-key/key.json @@ -0,0 +1,15 @@ +{ + "id": "key", + "public_key": { + "encrypted_data": "G/9a8qKJd8vFp6SPYW6Fm3hLUx1KjbHWsra03bM9sltKTabreznqqYGcLmcr\nRVg4ZZ3OzsOevlClhFPFoLwMguq2QCD5ttdHIoP231MX8AUhm89pPwqt7Wet\narClxd2XB8fNKyaf4xB5Fdh9ZM7H7aXVL+4UW4oxqxrm0bA5B+5UqwYlUF46\nsFmf9dOoJ1XyE2RfISJ5cigPcjqpH8osEY15jPZYZ9PrzzZTke+G32JGKyLp\nqjES0JCx192SBvWpxAIxCEgk3Vvdwz+9Ng3EMSuiIOI6crILSgVoYGysBBBs\nLJ8gv9mpa5YSRCPaGxEC+ssEELym80/dQSYIag7zNqBCDghKfxkTlI19cbkQ\nggMrN62sJ/ly0DwV+Lf4+l2hUOmaRuVlePMpcHp10rgET4wCZouAlL1zzzSq\ncqtUQJDSmFWm1xeq5noEvmGCBqzGIQBRc0J3v4sKSaohJZ3FciMgTys+2gfx\nRZP2Ch2Wb0zkkugzpo1MRYcoLRkAvei9TPysML3FlT2slMBwe3gwdz6DzfYg\nK4tFox4HDCtIxPA3yC4jM+4G4AtmGAImkn6Q\n", + "iv": "DbyIs1H8ClNSrbhCsoDKXg==\n", + "version": 1, + "cipher": "aes-256-cbc" + }, + "private_key": { + "encrypted_data": "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\n9UyfSdClvxfXayQEtOWbqACi\n", + "iv": "sjINJ/v1TAAwwsn96TzCNQ==\n", + "version": 1, + "cipher": "aes-256-cbc" + } +} \ No newline at end of file diff --git a/deploy_rsa b/deploy_rsa new file mode 100644 index 0000000..9e8111a --- /dev/null +++ b/deploy_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxmlnI8Q4rahslpfcO7TV1EArvtyPLFUpBhdTgI/10Imj5G9Z +IHWtNhErNRppzkBQ9cwrgc/I4/n6tPtiYg7h/aZYfIFQhMrxFfYggIlrHU8DZDQv +WkNj1K6WzsIiomHw+rHJvHK4lbhDmjk4qmmbosGlpOyHd2IqICXq5PVL1NiMdEy+ +WQ0EQNABuGRK8q6Pt7+reirE3vVzsVgGYwLlAIJmzq8FLORG7JAdPh0KRUOU5phJ +DMygm5zTGLgkJ8SnN72NnzNryCkTf1x4uRYiof4chdlFcPo+NjzW5Y1DPi42lmKF +BkDtwQovEvJJ7/hk934rfJV7LzDVvtHl82kgmwIDAQABAoIBAGJOIPsYophwJXUr +wsOWYoeqT8JeF1X4z72j2wyMx1dOivSIstkhVPO/5++Eqb22IibWxQupPritx6ja +ELHS++en/pmZnKJexrqB2zK0Y2z/FAgWzwti7liXNEM27uATtMkRQ4nqMUiFJoGI +LmpIrwMh/QdM5gWhjoulHc/ZNXw7+lo0lLsBa1KwgsZ217fmEQ0s4fgCl5FDiVlG +uEbpR5+1LJyrHFvcwz2fB7L3PJ9g0w5LCQQPiQIwu4wo4U5yD3BUUyNFPcY6+lvQ +rWvbBQaVUIEd7C3Swu7Expv0XON7Ai/BNIXo0MN0vBVzq6GjmWu86ovQsYpfMT19 +3ksLQzkCgYEA+KTfmdRjXmb906c6EiW4UzjbWWeI9Nb8oXWuJjCcfWj6g45LJTpt +b36mLTnVfmcsDvdYDKmuWYlJfb+44QzrFjfrWa+UbBdjwV3pXzJel043uzNJPA63 +k+pDX+XTw2mrIWc0AJhE/QzLLML8tIFPnGPzFrKWxE9AqzTYmIMg4C0CgYEAzEgX +QVxp9Wo3jlymIfJzp5rvhcbQxdacE9A0ZoLJ0TCTZ1Usp8QpKPpdSQTM4hLjIX/5 +3HpjKsc5mCY2koJxMxEWvgjczwTlAnsM7fygP4Kmdxg3zhEijEM5v2ZBdZ0ewxMi +qFsdey8E4l11ZdWZLDwbh2Jt7jg5jZhyq3qpOOcCgYBmNYAlAAWI/NVCd++LHi5T +J4AjlEfcPbPDu1hHIpxxgQHZqliBiS8LMgildqyoNUkLLenn6qhc7e5j3rfk6yaI +D5yTVXWxqTu8dRpFo7L2h5SpQz/LjFEyYI4pkZQnM/zA0meyBuX1D5lFYTH4EV/k +bcGzht4q/FkdB7AxoVWWUQKBgBEn/MwAduWlhgTuwwUW15+742HY0K/M1k7TrZLu +aQSoj7id7qIoV0yZujvnq01RSMIHfXSG0s1E7hFZJDwpuFgRl1deZyz7vD/5FZzn +Go466sAVyJoF1mDxRGhOrjCygWLguIhrHo655C8eqj1jMvoEEkvMeG8JScwagYbl +RlEBAoGBAKzWXsEbsLLjNyj8EmGJF+o74al38YdBY2zlyZ1VYCyTtXn0ZNxVi2iQ +ttwOgpEI+icGzDgU5AFkWlJIAouEGPmT+tAquce41ythm6lbg406/rJGtl01UnlQ +WQ+s0itRYeBnm6n7xqkk0p6f8CECUVthhOa+iZcYSOt5q9Ds5RvV +-----END RSA PRIVATE KEY----- diff --git a/deploy_rsa.pub b/deploy_rsa.pub new file mode 100644 index 0000000..b750933 --- /dev/null +++ b/deploy_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGaWcjxDitqGyWl9w7tNXUQCu+3I8sVSkGF1OAj/XQiaPkb1kgda02ESs1GmnOQFD1zCuBz8jj+fq0+2JiDuH9plh8gVCEyvEV9iCAiWsdTwNkNC9aQ2PUrpbOwiKiYfD6scm8criVuEOaOTiqaZuiwaWk7Id3YiogJerk9UvU2Ix0TL5ZDQRA0AG4ZEryro+3v6t6KsTe9XOxWAZjAuUAgmbOrwUs5EbskB0+HQpFQ5TmmEkMzKCbnNMYuCQnxKc3vY2fM2vIKRN/XHi5FiKh/hyF2UVw+j42PNbljUM+LjaWYoUGQO3BCi8S8knv+GT3fit8lXsvMNW+0eXzaSCb brun@Ivans-MacBook-Pro.local diff --git a/inhouse-cookbooks/chiliproject/attributes/default.rb b/inhouse-cookbooks/chiliproject/attributes/default.rb new file mode 100644 index 0000000..64b4e59 --- /dev/null +++ b/inhouse-cookbooks/chiliproject/attributes/default.rb @@ -0,0 +1,13 @@ +default["chiliproject"]["application"]["lvm_volume"] = "chiliproject-application" +default["chiliproject"]["application"]["lvm_group"] = "shared" +default["chiliproject"]["application"]["volume_size"] = "2GB" +default["chiliproject"]["application"]["rails_environment"] = "development" +default["chiliproject"]["application"]["application_user"] = "chiliproject" +default["chiliproject"]["application"]["application_directory"] = "/srv/chiliproject-application" +default["chiliproject"]["application"]["database_name"] = "chiliproject" +default["chiliproject"]["application"]["github_key"] = "" +default["chiliproject"]["application"]["github_secret"] = "" +default["chiliproject"]["application"]["create_partitions"] = "yes" +default["chiliproject"]["application"]['secret'] = '63c894873708c4d4a0d4ce7aa86b7b9638355d65082999bf806eae8d42a2c1f81de08df74349ebb1' +default["chiliproject"]["ruby_version"] = "1.9.3-p448" + diff --git a/inhouse-cookbooks/chiliproject/metadata.rb b/inhouse-cookbooks/chiliproject/metadata.rb new file mode 100644 index 0000000..152420e --- /dev/null +++ b/inhouse-cookbooks/chiliproject/metadata.rb @@ -0,0 +1 @@ +name "chiliproject" diff --git a/inhouse-cookbooks/chiliproject/recipes/default.rb b/inhouse-cookbooks/chiliproject/recipes/default.rb new file mode 100644 index 0000000..d3a1004 --- /dev/null +++ b/inhouse-cookbooks/chiliproject/recipes/default.rb @@ -0,0 +1,199 @@ +# +# Cookbook Name:: chiliproject +# Recipe:: application +# +# Copyright 2012, LLC Express 42 +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + + +class Chef::Recipe + include Express42::Base::Network +end + +if Chef::Config[:solo] + Chef::Log.warn("This recipe uses search. Chef Solo does not support search. I will return current node") + application_nodes = [ node ] +else + application_nodes = search(:node, "role:application AND chef_environment:#{node.chef_environment}") + frontend_nodes = search(:node, "role:chiliproject-frontend AND chef_environment:#{node.chef_environment}") +end + +application_servers_ip = application_nodes.map{|node| net_get_all_ip(node)}.flatten.uniq + +if frontend_nodes + frontend_servers_ip = frontend_nodes.map{|server| net_get_public(server)[0][1] } + frontend_servers_ip.flatten! +else + frontend_servers_ip = [] +end + +block_device = "/dev/#{node["chiliproject"]["application"]["lvm_group"]}/#{node["chiliproject"]["application"]["lvm_volume"]}" +user = node["chiliproject"]["application"]["application_user"] +application_directory = node["chiliproject"]["application"]["application_directory"] +rails_environment = node["chiliproject"]["application"]["rails_environment"] +lvm_volume = node["chiliproject"]["application"]["lvm_volume"] +lvm_group = node["chiliproject"]["application"]["lvm_group"] +lvm_size = node["chiliproject"]["application"]["volume_size"] + +%w(libpq5 libpq-dev imagemagick libmagickwand-dev).each { |p| package p } + +partition lvm_volume do + group lvm_group + size lvm_size + filesystem 'ext4' + mount_point application_directory + create_partition node["chiliproject"]["application"]["create_partitions"] == "yes" +end + +key = Chef::EncryptedDataBagItem.load('deploy-key', 'key') + +user_account user do + ssh_keys key['public_key'] +end + +directory application_directory do + owner user + group user +end + +directory "#{application_directory}/shared" do + owner user + group user +end + +directory "#{application_directory}/shared/config" do + owner user + group user +end + +link "/home/#{user}/#{lvm_volume}" do + to "#{application_directory}" +end + + +app_path = "/home/#{user}/#{lvm_volume}/current" + +ruby_install node["chiliproject"]["ruby_version"] do + action :install +end + +ruby_set node["chiliproject"]["ruby_version"] do + username user +end + +template "#{application_directory}/shared/config/database.yml" do + source 'database.yml.erb' + owner user + group user + variables :db_name => node["chiliproject"]["application"]["database_name"] +end + +template "#{application_directory}/shared/config/chiliproject.yml" do + source 'settings.yml.erb' + owner user + group user +end + +template "#{application_directory}/shared/config/Gemfile" do + owner user + group user +end + +template "#{application_directory}/shared/config/session_store.rb" do + owner user + group user + variables :secret => node["chiliproject"]["application"]["secret"] +end + +template "#{application_directory}/shared/config/unicorn.rb" do + source 'unicorn.rb.erb' + owner user + group user + variables :app_path => app_path, + :user => user, + :timeout => 30, + :worker_processes => 2, + :listen => "/tmp/chiliproject-rails.sock" +end + +sysctl( + "kernel.msgmax" => "65536", + "kernel.shmall" => "4294967296", + "kernel.shmmax" => "68719476736", + "kernel.msgmnb" => "65536", + "vm.swappiness" => "0", + "vm.overcommit_memory" => "0", + "fs.file-max" => "1048576" +) + +postgresql "main" do + databag "db" + configuration( + :version => "9.1", + :resources => { + :shared_buffers => "32MB", + :max_connections => 10 + } + ) + hba_configuration( + [ + { :type => "host", :database => "all", :user => "all", :address => "127.0.0.1/32", :method => "trust" }, + ] + ) +end + +runit_service "chiliproject_rails" do + template_name "chiliproject" + run_restart false + options :home_path => "/home/#{user}", + :app_path => app_path, + :target_user => user, + :target_ruby => "default", + :target_env => "production" +end + +sudo "chiliproject" do + user user + commands ["/usr/bin/sv * chiliproject_*"] + host "ALL" + nopasswd true +end + +nginx_site "chiliproject" do + variables :app_path => application_directory, + :application_servers_ip => application_servers_ip, + :frontend_servers_ip => frontend_servers_ip, + :backend => "unix:/tmp/chiliproject-rails.sock", + :vagrant_port => node["chiliproject"]["application"]["vagrant_port"] +end + +ssh_known_hosts_entry 'github.com' + +# key = Chef::EncryptedDataBagItem.load('deploy-key', 'key') +# +# file "/home/#{user}/.ssh/id_rsa" do +# content key['private_key'] +# owner user +# group user +# mode '0600' +# end + diff --git a/inhouse-cookbooks/chiliproject/templates/default/Gemfile.erb b/inhouse-cookbooks/chiliproject/templates/default/Gemfile.erb new file mode 100644 index 0000000..52fa5ad --- /dev/null +++ b/inhouse-cookbooks/chiliproject/templates/default/Gemfile.erb @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +source "https://rubygems.org" + +gem "rails", "2.3.18" +gem "unicorn" +gem "rake" + +gem "json", "~> 1.7.7" +gem "coderay", "~> 1.0.0" +gem "i18n", "~> 0.4.2" +gem "rubytree", "~> 0.5.2", :require => 'tree' +gem "rdoc", ">= 2.4.2" +gem "liquid", "~> 2.3.0" +gem "acts-as-taggable-on", "= 2.1.0" +gem 'gravatarify', '~> 3.0.0' +gem "tzinfo", "~> 0.3.31" # Fixes #903. Not required for Rails >= 3.2 + +group :test do + gem 'shoulda', '~> 2.10.3' + # Shoulda doesn't work nice on 1.9.3 and seems to need test-unit explicitely… + gem 'test-unit', :platforms => [:mri_19] + gem 'edavis10-object_daddy', :require => 'object_daddy' + gem 'mocha', '0.12.1' + gem 'capybara' + gem 'nokogiri' + gem 'coveralls', :require => false +end + +group :ldap do + gem "net-ldap", '~> 0.3.1' +end + +group :openid do + gem "ruby-openid", '~> 2.1.4', :require => 'openid' +end + +group :rmagick do + gem "rmagick", ">= 1.15.17" + # Older distributions might not have a sufficiently new ImageMagick version + # for the current rmagick release (current rmagick is rmagick 2, which + # requires ImageMagick 6.4.9 or later). If this is the case for you, comment + # the line above this comment block and uncomment the one underneath it to + # get an rmagick version known to work on older distributions. + # + # The following distributions are known to *not* ship with a usable + # ImageMagick version. There might be additional ones. + # * Ubuntu 9.10 and older + # * Debian Lenny 5.0 and older + # * CentOS 5 and older + # * RedHat 5 and older + # + #gem "rmagick", "< 2.0.0" +end + +# Use the commented pure ruby gems, if you have not the needed prerequisites on +# board to compile the native ones. Note, that their use is discouraged, since +# their integration is propbably not that well tested and their are slower in +# orders of magnitude compared to their native counterparts. You have been +# warned. + +platforms :mri, :mingw, :rbx do + # keep mysql group as backwards compat + group :mysql2, :mysql do + gem "mysql2", "~> 0.2.7" + end + + group :postgres do + gem "pg" + # gem "postgres-pr" + end + + group :sqlite do + gem "sqlite3" + end +end + +platforms :jruby do + gem "jruby-openssl" + + group :mysql do + gem "activerecord-jdbcmysql-adapter" + end + + group :postgres do + gem "activerecord-jdbcpostgresql-adapter" + end + + group :sqlite do + gem "activerecord-jdbcsqlite3-adapter" + end +end + +# Load a "local" Gemfile +gemfile_local = File.join(File.dirname(__FILE__), "Gemfile.local") +if File.readable?(gemfile_local) + puts "Loading #{gemfile_local} ..." if $DEBUG + instance_eval(File.read(gemfile_local)) +end + +# Load plugins' Gemfiles +["plugins", "chiliproject_plugins"].each do |plugin_path| + Dir.glob File.expand_path("../vendor/#{plugin_path}/*/Gemfile", __FILE__) do |file| + puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v` + instance_eval File.read(file) + end +end diff --git a/inhouse-cookbooks/chiliproject/templates/default/chiliproject.conf.erb b/inhouse-cookbooks/chiliproject/templates/default/chiliproject.conf.erb new file mode 100644 index 0000000..8298ec1 --- /dev/null +++ b/inhouse-cookbooks/chiliproject/templates/default/chiliproject.conf.erb @@ -0,0 +1,49 @@ +upstream chiliproject-unicorn-app { + server <%= @backend %>; +} + +server { + listen 8080; +# server_name deploy.fake.ru; + resolver_timeout 2s; + charset utf-8; + + root <%= @app_path %>/current/public/; + + <% @frontend_servers_ip.each do |ip| -%> + set_real_ip_from <%= ip %>; + <% end -%> + + access_log /var/log/nginx/strano-access.log; + error_log /var/log/nginx/strano-error.log; + client_max_body_size 256m; + gzip_static on; + + location / { + try_files $uri /system/maintenance.html @unicorn; + } + + location ^~ /system/ { + root <%= @app_path %>/current/public/; + } + + location @unicorn { + satisfy any; + + <% @application_servers_ip.each do |ip| -%> + allow <%= ip %>; + <% end -%> + + proxy_pass http://chiliproject-unicorn-app; + proxy_redirect off; + proxy_set_header Host $host<%= ":#{@vagrant_port}" if @vagrant_port -%>; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_read_timeout 300; + + proxy_buffer_size 16k; + proxy_buffers 32 16k; + } +} + diff --git a/inhouse-cookbooks/chiliproject/templates/default/database.yml.erb b/inhouse-cookbooks/chiliproject/templates/default/database.yml.erb new file mode 100644 index 0000000..b94ae01 --- /dev/null +++ b/inhouse-cookbooks/chiliproject/templates/default/database.yml.erb @@ -0,0 +1,12 @@ +# MySQL (default setup). Versions 4.1 and 5.0 are recommended. +# +# Get the fast C bindings: +# gem install mysql +# (on OS X: gem install mysql -- --include=/usr/local/lib) +# And be sure to use new-style password hashing: +# http://dev.mysql.com/doc/refman/5.0/en/old-client.html + +production: + adapter: postgresql + database: redmine + encoding: utf8 diff --git a/inhouse-cookbooks/chiliproject/templates/default/port_redmine.erb b/inhouse-cookbooks/chiliproject/templates/default/port_redmine.erb new file mode 100644 index 0000000..609c59b --- /dev/null +++ b/inhouse-cookbooks/chiliproject/templates/default/port_redmine.erb @@ -0,0 +1,2 @@ +# Redmine +-A FWR -p tcp -m tcp --dport 3000 -j ACCEPT \ No newline at end of file diff --git a/inhouse-cookbooks/chiliproject/templates/default/session_store.rb.erb b/inhouse-cookbooks/chiliproject/templates/default/session_store.rb.erb new file mode 100644 index 0000000..cc3154a --- /dev/null +++ b/inhouse-cookbooks/chiliproject/templates/default/session_store.rb.erb @@ -0,0 +1,21 @@ +# This file was generated by 'rake config/initializers/session_store.rb', +# and should not be made visible to public. +# If you have a load-balancing Redmine cluster, you will need to use the +# same version of this file on each machine. And be sure to restart your +# server when you modify this file. + +# Your secret key for verifying cookie session data integrity. If you +# change this key, all old sessions will become invalid! Make sure the +# secret is at least 30 characters and all random, no regular words or +# you'll be exposed to dictionary attacks. +ActionController::Base.session = { + :key => '_chiliproject_session', + # + # Uncomment and edit the :session_path below if are hosting your Redmine + # at a suburi and don't want the top level path to access the cookies + # + # See: http://www.redmine.org/issues/3968 + # + # :session_path => '/url_path_to/your/redmine/', + :secret => '<%= @secret %>' +} diff --git a/inhouse-cookbooks/chiliproject/templates/default/settings.yml.erb b/inhouse-cookbooks/chiliproject/templates/default/settings.yml.erb new file mode 100644 index 0000000..5006445 --- /dev/null +++ b/inhouse-cookbooks/chiliproject/templates/default/settings.yml.erb @@ -0,0 +1,142 @@ +# redMine - project management software +# Copyright (C) 2006-2007 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# DO NOT MODIFY THIS FILE !!! +# Settings can be defined through the application in Admin -> Settings + +app_title: + default: Redmine +app_subtitle: + default: Project management +welcome_text: + default: +login_required: + default: 0 +self_registration: + default: '2' +lost_password: + default: 1 +attachment_max_size: + format: int + default: 5120 +issues_export_limit: + format: int + default: 500 +activity_days_default: + format: int + default: 30 +per_page_options: + default: '25,50,100' +mail_from: + default: redmine@example.net +bcc_recipients: + default: 1 +plain_text_mail: + default: 0 +text_formatting: + default: textile +wiki_compression: + default: "" +default_language: + default: en +host_name: + default: localhost:3000 +protocol: + default: http +feeds_limit: + format: int + default: 15 +diff_max_lines_displayed: + format: int + default: 1500 +enabled_scm: + serialized: true + default: + - Subversion + - Darcs + - Mercurial + - Cvs + - Bazaar + - Git +autofetch_changesets: + default: 1 +sys_api_enabled: + default: 0 +commit_ref_keywords: + default: 'refs,references,IssueID' +commit_fix_keywords: + default: 'fixes,closes' +commit_fix_status_id: + format: int + default: 0 +commit_fix_done_ratio: + default: 100 +# autologin duration in days +# 0 means autologin is disabled +autologin: + format: int + default: 0 +# date format +date_format: + default: '' +time_format: + default: '' +user_format: + default: :firstname_lastname + format: symbol +cross_project_issue_relations: + default: 0 +notified_events: + serialized: true + default: + - issue_added + - issue_updated +mail_handler_api_enabled: + default: 0 +mail_handler_api_key: + default: +issue_list_default_columns: + serialized: true + default: + - tracker + - status + - priority + - subject + - assigned_to + - updated_on +display_subprojects_issues: + default: 1 +default_projects_public: + default: 1 +sequential_project_identifiers: + default: 0 +# encodings used to convert repository files content to UTF-8 +# multiple values accepted, comma separated +repositories_encodings: + default: '' +# encoding used to convert commit logs to UTF-8 +commit_logs_encoding: + default: 'UTF-8' +ui_theme: + default: '' +emails_footer: + default: |- + You have received this notification because you have either subscribed to it, or are involved in it. + To change your notification preferences, please click here: http://hostname/my/account +gravatar_enabled: + default: 0 diff --git a/inhouse-cookbooks/chiliproject/templates/default/sv-chiliproject-log-run.erb b/inhouse-cookbooks/chiliproject/templates/default/sv-chiliproject-log-run.erb new file mode 100644 index 0000000..c17845d --- /dev/null +++ b/inhouse-cookbooks/chiliproject/templates/default/sv-chiliproject-log-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +LOG_FOLDER=/var/log/runit/<%= @service_name %>/ +mkdir -p $LOG_FOLDER +exec svlogd -tt $LOG_FOLDER diff --git a/inhouse-cookbooks/chiliproject/templates/default/sv-chiliproject-run.erb b/inhouse-cookbooks/chiliproject/templates/default/sv-chiliproject-run.erb new file mode 100644 index 0000000..77ed8fa --- /dev/null +++ b/inhouse-cookbooks/chiliproject/templates/default/sv-chiliproject-run.erb @@ -0,0 +1,31 @@ +#!/bin/sh +set -e +ulimit -n 8192 + +HOME_PATH=<%= @options[:home_path] %> +APP_PATH=<%= @options[:app_path] %> +TARGET_USER=<%= @options[:target_user] %> +TARGET_RUBY=<%= @options[:target_ruby] == "default" ? "`cat /home/$TARGET_USER/.ruby-version`" : @options[:target_ruby] %> +RAILS_ENV=<%= @options[:target_env] %> +UNICORN_NAME=<%= @options[:unicorn_name] ? @options[:unicorn_name] : "unicorn" %> +UNICORN_PID_FILE=$APP_PATH/tmp/pids/$UNICORN_NAME.pid +CHRUBY_BIN=/opt/chruby/bin/chruby-exec + +test -f $UNICORN_PID_FILE && UNICORN_PID=`cat $UNICORN_PID_FILE` || UNICORN_PID=-1 + +cd $APP_PATH +EXISTING_PID=`pgrep -f 'unicorn_rails master'` && test "$EXISTING_PID" = "$UNICORN_PID" || HOME=$HOME_PATH chpst -u $TARGET_USER $CHRUBY_BIN $TARGET_RUBY -- bundle exec unicorn_rails -c config/$UNICORN_NAME.rb -E $RAILS_ENV + +# forward signals received in this wrapper to the Unicorn process: +for sig in HUP USR1 USR2 QUIT TERM QUIT +do + trap 'kill -'$sig' $(cat $UNICORN_PID_FILE)' $sig +done + +# loop forever while Unicorn has its pid file +while ( test -e $UNICORN_PID_FILE && ps -p `cat $UNICORN_PID_FILE` > /dev/null ) || ( test -e $UNICORN_PID_FILE.oldbin && ps -p `cat $UNICORN_PID_FILE.oldbin` > /dev/null ) +do + sleep 1 +done + + diff --git a/inhouse-cookbooks/chiliproject/templates/default/unicorn.rb.erb b/inhouse-cookbooks/chiliproject/templates/default/unicorn.rb.erb new file mode 100644 index 0000000..8172fd7 --- /dev/null +++ b/inhouse-cookbooks/chiliproject/templates/default/unicorn.rb.erb @@ -0,0 +1,42 @@ +WORK_DIR = "<%= @app_path %>" +<% if @prefix -%> +PREFIX = "<%= @prefix -%>-" +<% else -%> +PREFIX = "" +<% end -%> +worker_processes <%= @worker_processes %> +working_directory WORK_DIR +user "<%= @user %>" +preload_app true +timeout <%= @timeout %> +listen "<%= @listen %>", :backlog => 4096 + +pid "#{WORK_DIR}/tmp/pids/#{PREFIX}unicorn.pid" + +stderr_path "#{WORK_DIR}/log/#{PREFIX}unicorn.stderr.log" +stdout_path "#{WORK_DIR}/log/#{PREFIX}unicorn.stdout.log" + +before_exec do |_| + ENV["BUNDLE_GEMFILE"] = "#{WORK_DIR}/Gemfile" +end + +before_fork do |server, worker| + old_pid = "#{WORK_DIR}/tmp/pids/#{PREFIX}unicorn.pid.oldbin" + if File.exists?(old_pid) && server.pid != old_pid + begin + #Process.kill("QUIT", File.read(old_pid).to_i) + sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU + Process.kill(sig, File.read(old_pid).to_i) + rescue Errno::ENOENT, Errno::ESRCH + # someone else did our job for us + end + end + + defined?(ActiveRecord::Base) and + ActiveRecord::Base.connection.disconnect! +end + +after_fork do |server, worker| + defined?(ActiveRecord::Base) and + ActiveRecord::Base.establish_connection +end diff --git a/inhouse-cookbooks/fake/recipes/zabbix.rb b/inhouse-cookbooks/fake/recipes/zabbix.rb index 480cf46..83615c6 100644 --- a/inhouse-cookbooks/fake/recipes/zabbix.rb +++ b/inhouse-cookbooks/fake/recipes/zabbix.rb @@ -1,4 +1,4 @@ -zabbix_connect "connect to kupikupon zabbix" do +zabbix_connect "connect to zabbix" do apiurl "http://127.0.0.1/api_jsonrpc.php" user "Admin" password "zabbix" diff --git a/inhouse-cookbooks/strano/attributes/application.rb b/inhouse-cookbooks/strano/attributes/application.rb new file mode 100644 index 0000000..74419de --- /dev/null +++ b/inhouse-cookbooks/strano/attributes/application.rb @@ -0,0 +1,11 @@ +default["strano"]["application"]["lvm_volume"] = "strano-application" +default["strano"]["application"]["lvm_group"] = "shared" +default["strano"]["application"]["volume_size"] = "2GB" +default["strano"]["application"]["rails_environment"] = "production" +default["strano"]["application"]["application_user"] = "strano" +default["strano"]["application"]["application_directory"] = "/srv/strano-application" +default["strano"]["application"]["database_name"] = "strano" +default["strano"]["application"]["github_key"] = "" +default["strano"]["application"]["github_secret"] = "" +default["strano"]["application"]["create_partitions"] = "yes" +default["strano"]["ruby_version"] = "1.9.3-p448" diff --git a/inhouse-cookbooks/strano/metadata.rb b/inhouse-cookbooks/strano/metadata.rb new file mode 100644 index 0000000..7447b2b --- /dev/null +++ b/inhouse-cookbooks/strano/metadata.rb @@ -0,0 +1,8 @@ +maintainer "LLC Express 42" +maintainer_email "info@express42.com" +license "Copyright" +description "Deploy Strano Application" +version "0.0.1" +depends "sudo" +depends "rvm" +depends "lvm" diff --git a/inhouse-cookbooks/strano/recipes/application.rb b/inhouse-cookbooks/strano/recipes/application.rb new file mode 100644 index 0000000..ad13174 --- /dev/null +++ b/inhouse-cookbooks/strano/recipes/application.rb @@ -0,0 +1,225 @@ +# +# Cookbook Name:: strano +# Recipe:: application +# +# Copyright 2012, LLC Express 42 +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +class Chef::Recipe + include Express42::Base::Network +end + +if Chef::Config[:solo] + Chef::Log.warn("This recipe uses search. Chef Solo does not support search. I will return current node") + postgresql_master_node = node + application_nodes = [ node ] +else + postgresql_master_node = search(:node, "role:postgresql-master AND chef_environment:#{node.chef_environment}").first + application_nodes = search(:node, "role:application AND chef_environment:#{node.chef_environment}") + frontend_nodes = search(:node, "role:strano-frontend AND chef_environment:#{node.chef_environment}") +end + +postgresql_master_server = net_get_private(postgresql_master_node)[0][1] + +application_servers_ip = application_nodes.map{|node| net_get_all_ip(node)}.flatten.uniq + +if frontend_nodes + frontend_servers_ip = frontend_nodes.map{|server| net_get_public(server)[0][1] } + frontend_servers_ip.flatten! +else + frontend_servers_ip = [] +end + +block_device = "/dev/#{node["strano"]["application"]["lvm_group"]}/#{node["strano"]["application"]["lvm_volume"]}" +user = node["strano"]["application"]["application_user"] +application_directory = node["strano"]["application"]["application_directory"] +rails_environment = node["strano"]["application"]["rails_environment"] +lvm_volume = node["strano"]["application"]["lvm_volume"] +lvm_group = node["strano"]["application"]["lvm_group"] +lvm_size = node["strano"]["application"]["volume_size"] + +partition lvm_volume do + group lvm_group + size lvm_size + filesystem 'ext4' + mount_point application_directory + create_partition node["strano"]["application"]["create_partitions"] == "yes" +end + +user_account user + +directory "#{application_directory}/" do + owner user + group user +end + +link "/home/#{user}/#{lvm_volume}" do + to "#{application_directory}" +end + +ruby_install node["strano"]["ruby_version"] do + action :install +end + +ruby_set node["strano"]["ruby_version"] do + username user +end + +git "#{application_directory}/current" do + user user + group user + repository "git://github.com/express42/strano.git" + reference "v_0_1" + action :sync +end + +template "#{application_directory}/current/config/database.yml" do + source 'database.yml.erb' + owner user + group user + variables :db_name => node["strano"]["application"]["database_name"], + :password => "dbpassword", + :username => "dbuser", + :host => postgresql_master_server +end + +template "#{application_directory}/current/config/strano.yml" do + source 'strano.yml.erb' + owner user + group user +end + +template "#{application_directory}/current/config/unicorn.rb" do + source 'unicorn.rb.erb' + owner user + group user + variables :app_path => application_directory, + :worker_processes => 2, + :listen => "/tmp/strano-rails.sock" +end + +template "/home/#{user}/.ssh/config" do + source 'ssh_config' + owner user + group user +end + +execute "bundle install" do + cwd "#{application_directory}/current" + command "/opt/chruby/bin/chruby-exec #{node["strano"]["ruby_version"]} -- bundle install --deployment" + creates "#{application_directory}/current/deploy.lock" + user user + group user + environment 'HOME' => "/home/#{user}" +end + +sysctl( + "kernel.msgmax" => "65536", + "kernel.shmall" => "4294967296", + "kernel.shmmax" => "68719476736", + "kernel.msgmnb" => "65536", + "vm.swappiness" => "0", + "vm.overcommit_memory" => "0", + "fs.file-max" => "1048576" +) + +postgresql "main" do + databag "db" +end + +execute "db migrations" do + cwd "#{application_directory}/current" + command "/opt/chruby/bin/chruby-exec #{node["strano"]["ruby_version"]} -- bundle exec rake db:migrate" + creates "#{application_directory}/current/deploy.lock" + user user + group user + environment 'HOME' => "/home/#{user}", 'RAILS_ENV' => rails_environment +end + +execute "assets precompile" do + cwd "#{application_directory}/current" + command "/opt/chruby/bin/chruby-exec #{node["strano"]["ruby_version"]} -- bundle exec rake assets:precompile" + creates "#{application_directory}/current/deploy.lock" + user user + group user + environment 'HOME' => "/home/#{user}", 'RAILS_ENV' => rails_environment +end + +execute "create deploy lock" do + cwd "#{application_directory}/current" + command "touch #{application_directory}/current/deploy.lock" + user user + group user +end + +runit_service "strano_rails" do + template_name "rails_app" + run_restart false + options :home_path => "/home/#{user}", + :app_path => "#{application_directory}", + :target_user => user, + :target_ruby => "default", + :target_env => rails_environment +end + +template "#{node[:nginx][:directories][:conf_dir]}/strano-site-htpasswd" do + source "strano-site-htpasswd.erb" + owner "www-data" + group "www-data" + mode 0640 +end + +runit_service "strano-worker-1" do + run_restart false + template_name "strano-worker" + options "home_path" => "/home/#{user}", + "app_path" => application_directory, + "target_user" => user, + "target_ruby" => "default", + "target_env" => rails_environment +end + +sudo "strano" do + user user + commands ["/usr/bin/sv * strano_*"] + host "ALL" + nopasswd true +end + +nginx_site "nginx-strano-application" do + variables :app_path => application_directory, + :application_servers_ip => application_servers_ip, + :frontend_servers_ip => frontend_servers_ip, + :backend => "unix:/tmp/strano-rails.sock", + :vagrant_port => node["strano"]["application"]["vagrant_port"] +end + +ssh_known_hosts_entry 'github.com' + +key = Chef::EncryptedDataBagItem.load('deploy-key', 'key') + +file "/home/#{user}/.ssh/id_rsa" do + content key['private_key'] + owner user + group user + mode '0600' +end diff --git a/inhouse-cookbooks/strano/recipes/default.rb b/inhouse-cookbooks/strano/recipes/default.rb new file mode 100644 index 0000000..8acd013 --- /dev/null +++ b/inhouse-cookbooks/strano/recipes/default.rb @@ -0,0 +1,25 @@ +# +# Cookbook Name:: strano +# Recipe:: default +# +# Copyright 2012, LLC Express 42 +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# diff --git a/inhouse-cookbooks/strano/recipes/frontend.rb b/inhouse-cookbooks/strano/recipes/frontend.rb new file mode 100644 index 0000000..9a4522c --- /dev/null +++ b/inhouse-cookbooks/strano/recipes/frontend.rb @@ -0,0 +1,54 @@ +# +# Cookbook Name:: strano +# Recipe:: frontend +# +# Author:: LLC Express 42 (info@express42.com) +# +# Copyright (C) LLC 2012 Express 42 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +backend_servers = [] +backend_nodes = [] + +if Chef::Config[:solo] + Chef::Log.warn("This recipe uses search. Chef Solo does not support search. I will return current node") + backend_nodes = [ node ] +else + backend_nodes = search(:node, "role:strano-backend AND chef_environment:#{node.chef_environment}") +end + +backend_nodes.each do |item| + if net_get_public(item).empty? + backend_servers << [ net_get_private(item)[0][1], item[:fqdn] ] + else + backend_servers << [ net_get_public(item)[0][1], item[:fqdn] ] + end +end + +template "#{node[:nginx][:directories][:conf_dir]}/sites-available/nginx-frontend-strano.conf" do + source "nginx-frontend-strano.erb" + mode "0644" + variables :app_path => node["strano"]["application"]["application_directory"], + :backend_servers => backend_servers + notifies :reload, resources(:service => "nginx") +end + +nginx_site "nginx-frontend-strano" diff --git a/inhouse-cookbooks/strano/templates/default/database.yml.erb b/inhouse-cookbooks/strano/templates/default/database.yml.erb new file mode 100644 index 0000000..870eb46 --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/database.yml.erb @@ -0,0 +1,4 @@ +production: + adapter: postgresql + database: <%= @db_name %> + diff --git a/inhouse-cookbooks/strano/templates/default/nginx-frontend-strano.erb b/inhouse-cookbooks/strano/templates/default/nginx-frontend-strano.erb new file mode 100644 index 0000000..c7d5ac3 --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/nginx-frontend-strano.erb @@ -0,0 +1,43 @@ +upstream strano_backend { +<% @backend_servers.each do |server| %> + server <%= server[0] %> fail_timeout=60s; # Host: <%= server[1] %> +<% end %> +} + +server { + listen 80; + server_name _ ""; + return 301 $scheme://fake.ru$request_uri; + } + +server { + listen 80; + server_name ~^deploy\..+; + resolver_timeout 3s; + charset utf-8; + + root <%= @app_path %>/current/public/; + + access_log /var/log/nginx/strano-access.log; + error_log /var/log/nginx/strano-error.log; + client_max_body_size 256m; + gzip_static on; + + location / { + try_files $uri /system/maintenance.html @upstream; + } + + location @upstream { + + proxy_pass http://strano_backend; + proxy_redirect off; + 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_read_timeout 300; + + proxy_buffer_size 16k; + proxy_buffers 32 16k; + } +} diff --git a/inhouse-cookbooks/strano/templates/default/nginx-strano-application.conf.erb b/inhouse-cookbooks/strano/templates/default/nginx-strano-application.conf.erb new file mode 100644 index 0000000..d19fd28 --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/nginx-strano-application.conf.erb @@ -0,0 +1,48 @@ +upstream unicorn-app { + server <%= @backend %>; +} + +server { + listen 80; +# server_name deploy.fake.ru; + resolver_timeout 2s; + charset utf-8; + + root <%= @app_path %>/current/public/; + + <% @frontend_servers_ip.each do |ip| -%> + set_real_ip_from <%= ip %>; + <% end -%> + + access_log /var/log/nginx/strano-access.log; + error_log /var/log/nginx/strano-error.log; + client_max_body_size 256m; + gzip_static on; + + location / { + try_files $uri /system/maintenance.html @unicorn; + } + + location ^~ /system/ { + root <%= @app_path %>/current/public/; + } + + location @unicorn { + satisfy any; + + <% @application_servers_ip.each do |ip| -%> + allow <%= ip %>; + <% end -%> + + proxy_pass http://unicorn-app; + proxy_redirect off; + proxy_set_header Host $host<%= ":#{@vagrant_port}" if @vagrant_port -%>; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_read_timeout 300; + + proxy_buffer_size 16k; + proxy_buffers 32 16k; + } +} diff --git a/inhouse-cookbooks/strano/templates/default/ssh_config b/inhouse-cookbooks/strano/templates/default/ssh_config new file mode 100644 index 0000000..75d4c8a --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/ssh_config @@ -0,0 +1,3 @@ +Host github.com +User git +StrictHostKeyChecking false diff --git a/inhouse-cookbooks/strano/templates/default/strano-site-htpasswd.erb b/inhouse-cookbooks/strano/templates/default/strano-site-htpasswd.erb new file mode 100644 index 0000000..844205e --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/strano-site-htpasswd.erb @@ -0,0 +1 @@ +express42:$apr1$oc5y1WUf$9Rdm4v.itxrAX8DHVhCYm/ diff --git a/inhouse-cookbooks/strano/templates/default/strano.yml.erb b/inhouse-cookbooks/strano/templates/default/strano.yml.erb new file mode 100644 index 0000000..26cf4c8 --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/strano.yml.erb @@ -0,0 +1,6 @@ +production: + public_ssh_key: MYPUBLICKEY + github_key: <%= node["strano"]["application"]["github_key"] %> + github_secret: <%= node["strano"]["application"]["github_secret"] %> + allow_organizations: + allow_users: true diff --git a/inhouse-cookbooks/strano/templates/default/sv-rails_app-log-run.erb b/inhouse-cookbooks/strano/templates/default/sv-rails_app-log-run.erb new file mode 100644 index 0000000..c17845d --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/sv-rails_app-log-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +LOG_FOLDER=/var/log/runit/<%= @service_name %>/ +mkdir -p $LOG_FOLDER +exec svlogd -tt $LOG_FOLDER diff --git a/inhouse-cookbooks/strano/templates/default/sv-rails_app-run.erb b/inhouse-cookbooks/strano/templates/default/sv-rails_app-run.erb new file mode 100644 index 0000000..de92a3e --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/sv-rails_app-run.erb @@ -0,0 +1,30 @@ +#!/bin/sh +set -e + +HOME_PATH=<%= @options[:home_path] %> +APP_PATH=<%= @options[:app_path] %> +TARGET_USER=<%= @options[:target_user] %> +TARGET_RUBY=<%= @options[:target_ruby] == "default" ? "`cat /home/$TARGET_USER/.ruby-version`" : @options[:target_ruby] %> +RAILS_ENV=<%= @options[:target_env] %> +UNICORN_NAME=<%= @options[:unicorn_name] ? @options[:unicorn_name] : "unicorn" %> +UNICORN_PID_FILE=$APP_PATH/current/tmp/pids/unicorn.pid +CHRUBY_BIN=/opt/chruby/bin/chruby-exec + +test -f $UNICORN_PID_FILE && UNICORN_PID=`cat $UNICORN_PID_FILE` || UNICORN_PID=-1 + +export rvm_ignore_rvmrc=1 + +cd $APP_PATH/current +EXISTING_PID=`pgrep -f 'unicorn_rails master'` && test "$EXISTING_PID" = "$UNICORN_PID" || HOME=$HOME_PATH chpst -u $TARGET_USER $CHRUBY_BIN $TARGET_RUBY -- bundle exec unicorn_rails -c config/$UNICORN_NAME.rb -E $RAILS_ENV + +# forward signals received in this wrapper to the Unicorn process: +for sig in HUP USR1 USR2 QUIT TERM QUIT +do + trap 'kill -'$sig' $(cat $UNICORN_PID_FILE)' $sig +done + +# loop forever while Unicorn has its pid file +while ( test -e $UNICORN_PID_FILE && ps -p `cat $UNICORN_PID_FILE` > /dev/null ) || ( test -e $UNICORN_PID_FILE.oldbin && ps -p `cat $UNICORN_PID_FILE.oldbin` > /dev/null ) +do + sleep 1 +done diff --git a/inhouse-cookbooks/strano/templates/default/sv-strano-worker-log-run.erb b/inhouse-cookbooks/strano/templates/default/sv-strano-worker-log-run.erb new file mode 100644 index 0000000..8872b11 --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/sv-strano-worker-log-run.erb @@ -0,0 +1,4 @@ +#!/bin/sh +LOG_FOLDER=/var/log/runit/<%= @service_name %>/ +mkdir -p $LOG_FOLDER +exec svlogd -tt $LOG_FOLDER diff --git a/inhouse-cookbooks/strano/templates/default/sv-strano-worker-run.erb b/inhouse-cookbooks/strano/templates/default/sv-strano-worker-run.erb new file mode 100644 index 0000000..d2c1783 --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/sv-strano-worker-run.erb @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +HOME_PATH=<%= @options['home_path'] %> +APP_PATH=<%= @options['app_path'] %> +TARGET_USER=<%= @options['target_user'] %> +TARGET_RUBY=<%= @options['target_ruby'] == "default" ? "`cat /home/$TARGET_USER/.ruby-version`" : @options['target_ruby'] %> +<% if @options.has_key? 'memlimit' -%> +ulimit -v <%= @options['memlimit'] -%> +<% end -%> +CHRUBY_BIN=/opt/chruby/bin/chruby-exec + +export rvm_ignore_rvmrc=1 + +cd $APP_PATH/current + +HOME=$HOME_PATH RAILS_ENV="<%= @options['target_env'] %>" STDOUT=1 chpst -u $TARGET_USER $CHRUBY_BIN $TARGET_RUBY -- bundle exec rake bg:worker --trace diff --git a/inhouse-cookbooks/strano/templates/default/unicorn.rb.erb b/inhouse-cookbooks/strano/templates/default/unicorn.rb.erb new file mode 100644 index 0000000..2843116 --- /dev/null +++ b/inhouse-cookbooks/strano/templates/default/unicorn.rb.erb @@ -0,0 +1,74 @@ +# unicorn_rails -c /current/config/unicorn.rb -E production -D + +rails_env = ENV['RAILS_ENV'] || 'production' + +worker_processes <%= @worker_processes %> + +# Load rails+github.git into the master before forking workers +# for super-fast worker spawn times +preload_app true + +# Restart any workers that haven't responded in 120 seconds +timeout 120 + +# Listen on a Unix data socket +listen '<%= @listen %>', :backlog => 2048 + +stderr_path "<%= @app_path %>/current/log/unicorn.stderr.log" +stdout_path "<%= @app_path %>/current/log/unicorn.stdout.log" + + +## +# REE + +# http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow +#if GC.respond_to?(:copy_on_write_friendly=) +# GC.copy_on_write_friendly = true +#end + + +before_fork do |server, worker| + ## + # When sent a USR2, Unicorn will suffix its pidfile with .oldbin and + # immediately start loading up a new version of itself (loaded with a new + # version of our app). When this new Unicorn is completely loaded + # it will begin spawning workers. The first worker spawned will check to + # see if an .oldbin pidfile exists. If so, this means we've just booted up + # a new Unicorn and need to tell the old one that it can now die. To do so + # we send it a QUIT. + # + # Using this method we get 0 downtime deploys. + + old_pid = File.join(Rails.root, '/tmp/pids/unicorn.pid.oldbin') + if File.exists?(old_pid) && server.pid != old_pid + begin + Process.kill("QUIT", File.read(old_pid).to_i) + rescue Errno::ENOENT, Errno::ESRCH + # someone else did our job for us + end + end +end + + +after_fork do |server, worker| + ## + # Unicorn master loads the app then forks off workers - because of the way + # Unix forking works, we need to make sure we aren't using any of the parent's + # sockets, e.g. db connection + + ActiveRecord::Base.establish_connection + # CHIMNEY.client.connect_to_server + # Redis and Memcached would go here but their connections are established + # on demand, so the master never opens a socket + + # http://vccv.posterous.com/forking-processes-in-ruby-and-randomness - we've hit this issue with coupon generation + srand + + ## + # Unicorn master is started as root, which is fine, but let's + # drop the workers to git:git +end + +before_exec do |server| + ENV['BUNDLE_GEMFILE'] = "<%= @app_path %>/current/Gemfile" +end diff --git a/roles/chiliproject.rb b/roles/chiliproject.rb new file mode 100644 index 0000000..a0f6c4a --- /dev/null +++ b/roles/chiliproject.rb @@ -0,0 +1,12 @@ +name "chiliproject" +description "Install and configure chiliproject" +run_list "recipe[runit]", "recipe[postgresql]", "recipe[ruby]", "recipe[ssh_known_hosts]", "recipe[chiliproject]" + +default_attributes( + chiliproject: { + application: { + vagrant_port: 7070 + } + } +) + diff --git a/roles/strano.rb b/roles/strano.rb new file mode 100644 index 0000000..0ac6e9c --- /dev/null +++ b/roles/strano.rb @@ -0,0 +1,13 @@ +name "strano" +description "Install and configure redmine" +run_list "recipe[user]", "recipe[postgresql]", "recipe[partition]", "recipe[ruby]", "recipe[runit]", "recipe[nginx]", "recipe[ssh_known_hosts]", "recipe[strano::application]" + +default_attributes( + strano: { + application: { + github_key: "305bd9ae51bcd0190d9a", + github_secret: "ccc096152a559e27c9304bb9ae4f896b81a3832c", + vagrant_port: 7071 + } + } +)