cuoco 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +111 -0
- data/Rakefile +2 -0
- data/cuoco.gemspec +21 -0
- data/lib/cuoco.rb +8 -0
- data/lib/cuoco/bootstrapper.rb +26 -0
- data/lib/cuoco/capistrano.rb +44 -0
- data/lib/cuoco/capistrano/automatic.rb +5 -0
- data/lib/cuoco/config_generator.rb +10 -0
- data/lib/cuoco/runner.rb +30 -0
- data/lib/cuoco/uploader.rb +41 -0
- data/lib/cuoco/version.rb +3 -0
- data/templates/solo.rb.erb +37 -0
- metadata +99 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Leonid Shevtsov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# Cuoco provides plumbing necessary to run Chef Solo through Capistrano with zero configuration.
|
2
|
+
|
3
|
+
## Key values
|
4
|
+
|
5
|
+
* reuse as much server information from Capistrano as possible (including roles);
|
6
|
+
* comply to Chef directory structure as much as possible to simplify transition;
|
7
|
+
* bootstrap bare machines with minimal footprint.
|
8
|
+
|
9
|
+
## Summary
|
10
|
+
|
11
|
+
*Capistrano and Chef Solo, sitting in a tree...*
|
12
|
+
|
13
|
+
[Capistrano](https://github.com/capistrano/capistrano#capistrano) is a framework that allows you to run commands in parallel on multiple remote machines. It's primary use is application deployment, but Capistrano can automate any task you can do over SSH. A big advantage of Capistrano is that it is wildly popular among web developers, and thus well supported and rich with plugins.
|
14
|
+
|
15
|
+
Another common task you have to do on the remote machines is server provisioning/management, and Capistrano has no facilities for that. It's definitely possible to manage machines using Capistrano, but you have to bring your own scripts.
|
16
|
+
|
17
|
+
[Chef Solo](http://wiki.opscode.com/display/chef/Chef+Solo) is a tool that uses well-structured, data-driven Ruby scripts to describe and run configuration management routines. It runs locally and manages the machine it's running from. So before running Chef Solo, you have to install it and upload your scripts to that machine. You can do that by hand, you can use one of the many Chef Solo bootstrap scripts, or you can use some tool like littlechef, which needs to be configured to know about your servers.
|
18
|
+
|
19
|
+
But wait - not only Capistrano knows where your servers are, it can already run commands on them, and in parallel. It's exactly what Chef Solo is lacking.
|
20
|
+
|
21
|
+
Cuoco provides a set of Capistrano tasks to tie in Chef Solo into Capistrano.
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
gem install cuoco
|
26
|
+
|
27
|
+
If you're using bundler you already know the drill.
|
28
|
+
|
29
|
+
To make Cuoco tasks available in Capistrano, require this file in your `Capfile` or `config/deploy.rb`:
|
30
|
+
|
31
|
+
require 'cuoco/capistrano'
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
### Fully automatic mode
|
36
|
+
|
37
|
+
Cuoco can turn a completely bare machine into a live server with a single command. You buy servers, you declare them in Capistrano, you run `cap deploy:setup && cap deploy`. Done! [*]
|
38
|
+
|
39
|
+
To do so, require this file in your `Capfile` or `config/deploy.rb`.
|
40
|
+
|
41
|
+
require 'cuoco/capistrano/automatic'
|
42
|
+
|
43
|
+
It hooks up `cuoco:update_configuration` before `deploy:setup`. This built-in task, quote,
|
44
|
+
*"Prepares one or more servers for deployment."*. Which is what Chef does. If you
|
45
|
+
update your server configuration you run `cap deploy:setup` again, it is non-destructive.
|
46
|
+
|
47
|
+
My personal opinion is that running Chef on each deploy is wasteful and forces
|
48
|
+
sudo rights as a requirement for all deployers. If you really want to do that, you'll
|
49
|
+
have to figure out the (extremely tricky) Capistrano directive yourself.
|
50
|
+
|
51
|
+
[*] Naturally, you have to write the provisioning scripts first.
|
52
|
+
|
53
|
+
### Manual mode
|
54
|
+
|
55
|
+
There are separate Cuoco tasks defined for more granular control.
|
56
|
+
|
57
|
+
cuoco:bootstrap # Installs chef, but does not run anything
|
58
|
+
cuoco:run # Runs chef assuming it's already installed
|
59
|
+
|
60
|
+
### Usage without deployment
|
61
|
+
|
62
|
+
If application deployment doesn't apply in your scenario, you can just run
|
63
|
+
|
64
|
+
cap cuoco:update_configuration
|
65
|
+
|
66
|
+
directly.
|
67
|
+
|
68
|
+
## Minimal requirements for remote machines
|
69
|
+
|
70
|
+
Here are the minimal requirements for running Cuoco:
|
71
|
+
|
72
|
+
* the server is running [one of the operating systems supported by Omnibus](http://wiki.opscode.com/display/chef/Installing+Omnibus+Chef+Client+on+Linux+and+Mac#InstallingOmnibusChefClientonLinuxandMac-TestedOperatingSystems) (most flavors of Linux will do);
|
73
|
+
* Capistrano can connect to the server;
|
74
|
+
* the Capistrano user has sudo rights on the server. See [my article on setting up user accounts](http://leonid.shevtsov.me/en/how-to-set-up-user-accounts-on-your-web-server) for the suggested approach.
|
75
|
+
|
76
|
+
## Configuration
|
77
|
+
|
78
|
+
Cuoco works with a traditional Chef directory structure. By default it looks for it in `config/chef`.
|
79
|
+
You can change that:
|
80
|
+
|
81
|
+
# set the root Chef directory
|
82
|
+
set :chef_path, 'chef'
|
83
|
+
|
84
|
+
### Roles
|
85
|
+
|
86
|
+
**Cuoco assigns Chef roles from Capistrano roles.**
|
87
|
+
|
88
|
+
This means that every server that you will run `cuoco:update_configuration` on will
|
89
|
+
get its own run list based on the roles it has: if it has `:app` and `:db` roles, Chef's run list for that server will be `role[app], role[db]`.
|
90
|
+
|
91
|
+
The chef roles directory *must* contain a role file for every Capistrano role you are using.
|
92
|
+
|
93
|
+
### Variables
|
94
|
+
|
95
|
+
TODO
|
96
|
+
|
97
|
+
### Environments
|
98
|
+
|
99
|
+
TODO Environments are not supported by Chef Solo. It would be nice to provide the functionality based on Capistrano stages.
|
100
|
+
|
101
|
+
## What's in a name?
|
102
|
+
|
103
|
+
"Cuoco" means "cook" in Italian. The original [Capistrano is a city in Italy](https://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=Capistrano,+Vibo+Valentia,+Italy&aq=0&oq=capistrano,+italy&sll=37.0625,-95.677068&sspn=60.376022,135.263672&vpsrc=0&t=h&ie=UTF8&hq=&hnear=Capistrano,+Province+of+Vibo+Valentia,+Calabria,+Italy&z=16). So a chef working solo in Capistrano would be called *un cuoco*... get it now?
|
104
|
+
|
105
|
+
(If you've been expecting to see Kaley Cuoco here, I'll save you a trip to Reddit:)
|
106
|
+
|
107
|
+
![Another Cuoco](http://i.imgur.com/u5OIil.jpg)
|
108
|
+
|
109
|
+
* * *
|
110
|
+
|
111
|
+
2012 [Leonid Shevtsov](http://leonid.shevtsov.me)
|
data/Rakefile
ADDED
data/cuoco.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/cuoco/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Leonid Shevtsov"]
|
6
|
+
gem.email = ["leonid@shevtsov.me"]
|
7
|
+
gem.description = %q{Run Chef Solo from Capistrano}
|
8
|
+
gem.summary = %q{Use Capistrano for server provisioning and configuration management, not only application deployment.}
|
9
|
+
gem.homepage = "https://github.com/leonid-shevtsov/Cuoco"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "cuoco"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Cuoco::VERSION
|
17
|
+
|
18
|
+
gem.add_runtime_dependency 'capistrano', '>= 2'
|
19
|
+
|
20
|
+
gem.add_development_dependency 'rake'
|
21
|
+
end
|
data/lib/cuoco.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Cuoco
|
2
|
+
class Bootstrapper
|
3
|
+
BOOTSTRAP_COMMAND = "\
|
4
|
+
if ! command -v chef-solo &>/dev/null;\
|
5
|
+
then\
|
6
|
+
curl -L http://opscode.com/chef/install.sh | sudo -p \"sudo password: \" bash;\
|
7
|
+
if ! command -v chef-solo &>/dev/null;\
|
8
|
+
then\
|
9
|
+
false;\
|
10
|
+
else\
|
11
|
+
true;\
|
12
|
+
fi;\
|
13
|
+
else\
|
14
|
+
true;\
|
15
|
+
fi\
|
16
|
+
".gsub(/ +/,' ')
|
17
|
+
|
18
|
+
def initialize(capistrano)
|
19
|
+
@cap = capistrano
|
20
|
+
end
|
21
|
+
|
22
|
+
def bootstrap
|
23
|
+
@cap.run(BOOTSTRAP_COMMAND, :shell => '/bin/bash')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'cuoco'
|
2
|
+
|
3
|
+
::Capistrano::Configuration.instance(:must_exist).load do
|
4
|
+
namespace :cuoco do
|
5
|
+
desc "Configures remote servers using Chef Solo"
|
6
|
+
task :update_configuration do
|
7
|
+
bootstrap
|
8
|
+
run_roles
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Sets up Chef on remote server"
|
12
|
+
task :bootstrap do
|
13
|
+
bootstrapper = Cuoco::Bootstrapper.new(self)
|
14
|
+
bootstrapper.bootstrap
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Uploads Cuoco and Chef files to remote servers"
|
18
|
+
task :upload_files do
|
19
|
+
uploader = Cuoco::Uploader.new(self)
|
20
|
+
uploader.upload
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Executes Chef Solo with role-based run lists"
|
24
|
+
task :run_roles do
|
25
|
+
upload_files
|
26
|
+
|
27
|
+
runner = Cuoco::Runner.new(self)
|
28
|
+
runner.run
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Executes Chef Solo with a custom run list"
|
32
|
+
task :run_list do
|
33
|
+
list = fetch(:chef_run_list, [])
|
34
|
+
if list.empty?
|
35
|
+
abort ":chef_run_list is empty"
|
36
|
+
end
|
37
|
+
|
38
|
+
upload_files
|
39
|
+
|
40
|
+
runner = Cuoco::Runner.new(self)
|
41
|
+
runner.run_list( list )
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/cuoco/runner.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Cuoco
|
2
|
+
class Runner
|
3
|
+
# $CAPISTRANO:HOSTROLES$ is a magic Capistrano placeholder that gets replaced
|
4
|
+
# with a comma-delimited list of roles *for each specific server individually*.
|
5
|
+
# See Capistrano::Command#replace_placeholders
|
6
|
+
# The awk command translates "app,web,db" into "role[app],role[web],role[db]" to pass them to Chef
|
7
|
+
SERVER_ROLES = %Q{`echo '$CAPISTRANO:HOSTROLES$' | awk '{sub(/^/,"role["); sub(/$/,"]"); gsub(/,/,"],role["); print}'`}
|
8
|
+
|
9
|
+
def initialize(capistrano)
|
10
|
+
@cap = capistrano
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
run_list(SERVER_ROLES)
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_list(roles_and_recipes)
|
18
|
+
if roles_and_recipes.is_a? Array
|
19
|
+
roles_and_recipes = roles_and_recipes.join(',')
|
20
|
+
end
|
21
|
+
|
22
|
+
@cap.sudo(chef_command(roles_and_recipes), :shell => '/bin/bash')
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def chef_command(roles_and_recipes)
|
27
|
+
"chef-solo -j /tmp/cuoco/node.json -o #{roles_and_recipes}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'cuoco/config_generator'
|
2
|
+
|
3
|
+
module Cuoco
|
4
|
+
# This class handles uploading Cuoco and Chef scripts onto the remote machines
|
5
|
+
class Uploader
|
6
|
+
def initialize(capistrano)
|
7
|
+
@cap = capistrano
|
8
|
+
|
9
|
+
@chef_path = File.expand_path(@cap.fetch(:chef_path, 'config/chef'))
|
10
|
+
|
11
|
+
@cuoco_remote_path = '/tmp/cuoco'
|
12
|
+
end
|
13
|
+
|
14
|
+
def upload
|
15
|
+
prepare_directory_structure
|
16
|
+
upload_chef_config
|
17
|
+
upload_chef_files
|
18
|
+
upload_node_json
|
19
|
+
end
|
20
|
+
|
21
|
+
def prepare_directory_structure
|
22
|
+
@cap.run("mkdir -p #{@cuoco_remote_path}", :shell => '/bin/bash')
|
23
|
+
end
|
24
|
+
|
25
|
+
def upload_chef_config
|
26
|
+
@config_contents = Cuoco::ConfigGenerator.chef_config(binding)
|
27
|
+
@cap.put(@config_contents, @cuoco_remote_path+'/solo.rb')
|
28
|
+
@cap.sudo("mv #{@cuoco_remote_path}/solo.rb /etc/chef/solo.rb", :shell => '/bin/bash')
|
29
|
+
end
|
30
|
+
|
31
|
+
def upload_chef_files
|
32
|
+
@cap.sudo("rm -rf #{@cuoco_remote_path}/chef", :shell => '/bin/bash')
|
33
|
+
@cap.upload(@chef_path, @cuoco_remote_path+'/chef', :recursive => true)
|
34
|
+
@cap.run("#{@cap.sudo} rm -rf /var/chef && #{@cap.sudo} mv #{@cuoco_remote_path}/chef /var/chef && #{@cap.sudo} chown -R root:root /var/chef", :shell => '/bin/bash')
|
35
|
+
end
|
36
|
+
|
37
|
+
def upload_node_json
|
38
|
+
@cap.put('{}', @cuoco_remote_path+'/node.json')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Chef-solo config generated by Cuoco at <%=Time.now.to_s%>
|
2
|
+
|
3
|
+
<% if @node_name %>
|
4
|
+
node_name <%=@node_name.inspect %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
# Paths
|
8
|
+
|
9
|
+
<% if @cookbook_paths %>
|
10
|
+
cookbook_path <%=@cookbook_paths.inspect %>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<% if @data_bag_path %>
|
14
|
+
data_bag_path <%=@data_bag_path.inspect %>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<% if @role_path %>
|
18
|
+
role_path <%=@role_path.inspect %>
|
19
|
+
<% end %>
|
20
|
+
|
21
|
+
<% if @node_path %>
|
22
|
+
node_path <%=@node_path.inspect %>
|
23
|
+
<% end %>
|
24
|
+
|
25
|
+
<% if @file_backup_path %>
|
26
|
+
file_backup_path <%=@file_backup_path.inspect %>
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
# Logging
|
30
|
+
|
31
|
+
<% if @log_level %>
|
32
|
+
log_level <%=@log_level.inspect %>
|
33
|
+
<% end %>
|
34
|
+
|
35
|
+
<% if @verbose_logging != nil %>
|
36
|
+
verbose_logging <%=@verbose_logging.inspect%>
|
37
|
+
<% end %>
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cuoco
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Leonid Shevtsov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: capistrano
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Run Chef Solo from Capistrano
|
47
|
+
email:
|
48
|
+
- leonid@shevtsov.me
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- .gitignore
|
54
|
+
- Gemfile
|
55
|
+
- LICENSE
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- cuoco.gemspec
|
59
|
+
- lib/cuoco.rb
|
60
|
+
- lib/cuoco/bootstrapper.rb
|
61
|
+
- lib/cuoco/capistrano.rb
|
62
|
+
- lib/cuoco/capistrano/automatic.rb
|
63
|
+
- lib/cuoco/config_generator.rb
|
64
|
+
- lib/cuoco/runner.rb
|
65
|
+
- lib/cuoco/uploader.rb
|
66
|
+
- lib/cuoco/version.rb
|
67
|
+
- templates/solo.rb.erb
|
68
|
+
homepage: https://github.com/leonid-shevtsov/Cuoco
|
69
|
+
licenses: []
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
hash: -3430509962393590737
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
hash: -3430509962393590737
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 1.8.24
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: Use Capistrano for server provisioning and configuration management, not
|
98
|
+
only application deployment.
|
99
|
+
test_files: []
|