mirror of
https://github.com/kemko/bw.git
synced 2026-01-01 15:45:44 +03:00
Practice 7
This commit is contained in:
8
Cheffile
8
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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
4
Vagrantfile
vendored
4
Vagrantfile
vendored
@@ -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
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
{
|
||||
"id": "databases",
|
||||
"databases": {
|
||||
"chiliproject": {
|
||||
"options": {
|
||||
"owner": "chiliproject"
|
||||
}
|
||||
},
|
||||
"redmine": {
|
||||
"options": {
|
||||
"owner": "redmine"
|
||||
}
|
||||
},
|
||||
"strano": {
|
||||
"options": {
|
||||
"owner": "strano"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"id": "users",
|
||||
"users": {
|
||||
"redmine": {}
|
||||
"redmine": {},
|
||||
"chiliproject": {},
|
||||
"strano": {}
|
||||
}
|
||||
}
|
||||
|
||||
15
data_bags/deploy-key/key.json
Normal file
15
data_bags/deploy-key/key.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
27
deploy_rsa
Normal file
27
deploy_rsa
Normal file
@@ -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-----
|
||||
1
deploy_rsa.pub
Normal file
1
deploy_rsa.pub
Normal file
@@ -0,0 +1 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGaWcjxDitqGyWl9w7tNXUQCu+3I8sVSkGF1OAj/XQiaPkb1kgda02ESs1GmnOQFD1zCuBz8jj+fq0+2JiDuH9plh8gVCEyvEV9iCAiWsdTwNkNC9aQ2PUrpbOwiKiYfD6scm8criVuEOaOTiqaZuiwaWk7Id3YiogJerk9UvU2Ix0TL5ZDQRA0AG4ZEryro+3v6t6KsTe9XOxWAZjAuUAgmbOrwUs5EbskB0+HQpFQ5TmmEkMzKCbnNMYuCQnxKc3vY2fM2vIKRN/XHi5FiKh/hyF2UVw+j42PNbljUM+LjaWYoUGQO3BCi8S8knv+GT3fit8lXsvMNW+0eXzaSCb brun@Ivans-MacBook-Pro.local
|
||||
13
inhouse-cookbooks/chiliproject/attributes/default.rb
Normal file
13
inhouse-cookbooks/chiliproject/attributes/default.rb
Normal file
@@ -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"
|
||||
|
||||
1
inhouse-cookbooks/chiliproject/metadata.rb
Normal file
1
inhouse-cookbooks/chiliproject/metadata.rb
Normal file
@@ -0,0 +1 @@
|
||||
name "chiliproject"
|
||||
199
inhouse-cookbooks/chiliproject/recipes/default.rb
Normal file
199
inhouse-cookbooks/chiliproject/recipes/default.rb
Normal file
@@ -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
|
||||
|
||||
106
inhouse-cookbooks/chiliproject/templates/default/Gemfile.erb
Normal file
106
inhouse-cookbooks/chiliproject/templates/default/Gemfile.erb
Normal file
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
# Redmine
|
||||
-A FWR -p tcp -m tcp --dport 3000 -j ACCEPT
|
||||
@@ -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 %>'
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
LOG_FOLDER=/var/log/runit/<%= @service_name %>/
|
||||
mkdir -p $LOG_FOLDER
|
||||
exec svlogd -tt $LOG_FOLDER
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
11
inhouse-cookbooks/strano/attributes/application.rb
Normal file
11
inhouse-cookbooks/strano/attributes/application.rb
Normal file
@@ -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"
|
||||
8
inhouse-cookbooks/strano/metadata.rb
Normal file
8
inhouse-cookbooks/strano/metadata.rb
Normal file
@@ -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"
|
||||
225
inhouse-cookbooks/strano/recipes/application.rb
Normal file
225
inhouse-cookbooks/strano/recipes/application.rb
Normal file
@@ -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
|
||||
25
inhouse-cookbooks/strano/recipes/default.rb
Normal file
25
inhouse-cookbooks/strano/recipes/default.rb
Normal file
@@ -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.
|
||||
#
|
||||
54
inhouse-cookbooks/strano/recipes/frontend.rb
Normal file
54
inhouse-cookbooks/strano/recipes/frontend.rb
Normal file
@@ -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"
|
||||
@@ -0,0 +1,4 @@
|
||||
production:
|
||||
adapter: postgresql
|
||||
database: <%= @db_name %>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
3
inhouse-cookbooks/strano/templates/default/ssh_config
Normal file
3
inhouse-cookbooks/strano/templates/default/ssh_config
Normal file
@@ -0,0 +1,3 @@
|
||||
Host github.com
|
||||
User git
|
||||
StrictHostKeyChecking false
|
||||
@@ -0,0 +1 @@
|
||||
express42:$apr1$oc5y1WUf$9Rdm4v.itxrAX8DHVhCYm/
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
LOG_FOLDER=/var/log/runit/<%= @service_name %>/
|
||||
mkdir -p $LOG_FOLDER
|
||||
exec svlogd -tt $LOG_FOLDER
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
LOG_FOLDER=/var/log/runit/<%= @service_name %>/
|
||||
mkdir -p $LOG_FOLDER
|
||||
exec svlogd -tt $LOG_FOLDER
|
||||
@@ -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
|
||||
74
inhouse-cookbooks/strano/templates/default/unicorn.rb.erb
Normal file
74
inhouse-cookbooks/strano/templates/default/unicorn.rb.erb
Normal file
@@ -0,0 +1,74 @@
|
||||
# unicorn_rails -c <path_to>/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
|
||||
12
roles/chiliproject.rb
Normal file
12
roles/chiliproject.rb
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
13
roles/strano.rb
Normal file
13
roles/strano.rb
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user