gusteau 0.4.8 → 1.0.0.dev
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +2 -0
- data/CHANGELOG.md +4 -0
- data/README.md +31 -30
- data/bin/gusteau +14 -13
- data/gusteau.gemspec +4 -1
- data/lib/gusteau.rb +1 -0
- data/lib/gusteau/bureau.rb +32 -20
- data/lib/gusteau/chef.rb +2 -2
- data/lib/gusteau/config.rb +39 -0
- data/lib/gusteau/node.rb +11 -43
- data/lib/gusteau/ssh_config.rb +8 -7
- data/lib/gusteau/vagrant.rb +7 -32
- data/lib/gusteau/version.rb +1 -1
- data/spec/config/gusteau.yml +60 -0
- data/spec/config/remi.yml +29 -0
- data/spec/lib/gusteau/bureau_spec.rb +52 -0
- data/spec/lib/gusteau/compressed_tar_stream_spec.rb +31 -0
- data/spec/lib/gusteau/config_spec.rb +31 -0
- data/spec/lib/gusteau/log_spec.rb +34 -0
- data/spec/lib/gusteau/node_spec.rb +40 -85
- data/spec/lib/gusteau/server_spec.rb +12 -0
- data/spec/lib/gusteau/ssh_config_spec.rb +16 -10
- data/spec/lib/gusteau/ssh_spec.rb +110 -0
- data/spec/lib/gusteau/vagrant_spec.rb +46 -17
- data/spec/spec_helper.rb +11 -0
- data/template/.gusteau.yml.erb +21 -0
- data/template/.kitchen.yml +20 -0
- data/template/Berksfile +3 -3
- data/template/Gemfile +6 -1
- data/template/README.md.erb +70 -0
- data/template/Vagrantfile +12 -4
- data/template/init.sh +27 -0
- data/template/site-cookbooks/cowsay/metadata.rb +10 -0
- data/template/site-cookbooks/cowsay/recipes/default.rb +1 -3
- data/template/site-cookbooks/platform/metadata.rb +14 -0
- data/template/site-cookbooks/platform/recipes/default.rb +3 -0
- data/template/test/integration/data_bags/users/remi.json +7 -0
- data/template/test/integration/default/serverspec/localhost/cowsay_spec.rb +5 -0
- data/template/test/integration/default/serverspec/localhost/platform_spec.rb +25 -0
- data/template/test/integration/default/serverspec/spec_helper.rb +9 -0
- metadata +81 -17
- data/bootstrap/centos.sh +0 -17
- data/bootstrap/redhat.sh +0 -17
- data/bootstrap/ubuntu.sh +0 -17
- data/spec/nodes/development.yml +0 -17
- data/spec/nodes/production.yml +0 -18
- data/spec/nodes/staging.yml +0 -12
- data/template/nodes/example.yml.erb +0 -19
- data/template/roles/platform.rb +0 -8
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -4,12 +4,13 @@ Gusteau
|
|
4
4
|
*"Anyone can cook."*
|
5
5
|
|
6
6
|
[![Build Status](https://www.travis-ci.org/locomote/gusteau.png?branch=master)](https://www.travis-ci.org/locomote/gusteau)
|
7
|
+
[![Coverage Status](https://coveralls.io/repos/locomote/gusteau/badge.png)](https://coveralls.io/r/locomote/gusteau)
|
7
8
|
[![Dependency Status](https://gemnasium.com/locomote/gusteau.png)](https://gemnasium.com/locomote/gusteau)
|
8
9
|
|
9
10
|
Introduction
|
10
11
|
------------
|
11
12
|
|
12
|
-
Gusteau is
|
13
|
+
Gusteau is an easy to use configuration manager for Chef Solo and Vagrant. It provides an efficient interface to Chef Solo as well as some nice features:
|
13
14
|
|
14
15
|
* Uses YAML for readable server configuration definitions
|
15
16
|
* Uses a single SSH connection to stream compressed files and commands
|
@@ -24,60 +25,60 @@ Gettings started
|
|
24
25
|
Gusteau is a Ruby gem:
|
25
26
|
|
26
27
|
```
|
27
|
-
gem install gusteau
|
28
|
+
gem install gusteau --pre
|
28
29
|
```
|
29
30
|
|
30
|
-
A typical Gusteau
|
31
|
+
A typical Gusteau configuration looks like this:
|
31
32
|
|
32
33
|
```
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
environments:
|
35
|
+
development:
|
36
|
+
attributes:
|
37
|
+
mysql:
|
38
|
+
server_root_password: ASahiweqwqe2
|
39
|
+
rvm:
|
40
|
+
default_ruby: 1.9.3-p362
|
41
|
+
users:
|
42
|
+
- linguini
|
40
43
|
|
41
|
-
|
42
|
-
|
44
|
+
run_list:
|
45
|
+
- role[base]
|
46
|
+
- recipe[mysql::server]
|
47
|
+
- recipe[iptables]
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
server:
|
49
|
-
host: 33.33.33.20
|
50
|
-
platform: ubuntu
|
51
|
-
password: omgsecret
|
49
|
+
nodes:
|
50
|
+
playground:
|
51
|
+
host: 33.33.33.20
|
52
|
+
password: omgsecret
|
52
53
|
```
|
53
54
|
|
54
|
-
Gusteau only needs a node definition to run, but you'll need a few cookbooks to actually cook something :)
|
55
|
+
Gusteau only needs a single node definition to run, but you'll need a few cookbooks to actually cook something :)
|
55
56
|
The following command generates an example configuration to get you started:
|
56
57
|
|
57
58
|
```
|
58
59
|
gusteau init project-name
|
59
60
|
```
|
60
61
|
|
61
|
-
Next, `cd project-name` and see
|
62
|
+
Next, `cd project-name` and see `.gusteau.yml`.
|
62
63
|
|
63
64
|
|
64
|
-
|
65
|
+
Converging a server
|
65
66
|
----------
|
66
67
|
|
67
68
|
The following command will run all roles and recipes from node's YAML file.
|
68
69
|
|
69
70
|
```
|
70
|
-
gusteau
|
71
|
+
gusteau converge development-playground
|
71
72
|
```
|
72
73
|
|
73
74
|
Use the `--bootstrap` or `-b` flag to bootstrap chef-solo (for the first time run).
|
74
75
|
|
75
|
-
|
76
|
+
Applying individual recipes
|
76
77
|
-----------
|
77
|
-
You may choose to run a
|
78
|
+
You may choose to run a custom run_list instead of the full convergence.
|
78
79
|
|
79
80
|
```
|
80
|
-
gusteau
|
81
|
+
gusteau apply development-playground "role[base],recipe[oh-my-zsh]"
|
81
82
|
```
|
82
83
|
|
83
84
|
SSH
|
@@ -85,7 +86,7 @@ SSH
|
|
85
86
|
Gusteau provides a useful shortcut that you may use to ssh into a node. If you haven't got passwordless authentication set up, Gusteau will use `user` and `password` values from the node configuration.
|
86
87
|
|
87
88
|
```
|
88
|
-
gusteau ssh
|
89
|
+
gusteau ssh development-playground
|
89
90
|
```
|
90
91
|
|
91
92
|
Please note that `expect` utility must be installed for `gusteau ssh` to work.
|
@@ -123,7 +124,7 @@ end
|
|
123
124
|
|
124
125
|
* The `prefix` option lets you prepend your VirtualBox VMs names, e.g. `loco-nodename`.
|
125
126
|
* The `defaults` one lets you provide default values for `cpus`, `memory`, `box_url`.
|
126
|
-
* If you'd like to use Vagrant's own automatic `chef_solo` provisioner, set `provision` to `true`.
|
127
|
+
* If you'd like to use Vagrant's own automatic `chef_solo` provisioner, set `provision` to `true`.
|
127
128
|
|
128
129
|
Please note that the add-on only works with Vagrant ~> 1.2 and needs gusteau to be installed as a Vagrant plugin:
|
129
130
|
|
@@ -135,5 +136,5 @@ Notes
|
|
135
136
|
-----
|
136
137
|
|
137
138
|
* Feel free to contribute a [bootstrap script](https://github.com/locomote/gusteau/tree/master/bootstrap) for your platform.
|
138
|
-
* Gusteau uploads
|
139
|
+
* Gusteau uploads `./cookbooks` and `./site-cookbooks` from the current working directory.
|
139
140
|
|
data/bin/gusteau
CHANGED
@@ -13,14 +13,14 @@ class Gusteau::CLI < Optitron::CLI
|
|
13
13
|
class_opt 'log_level', 'Set the log level', :in => %w{debug info warn error fatal}
|
14
14
|
class_opt 'why-run', 'Enable whyrun mode', :short_name => 'W'
|
15
15
|
|
16
|
-
desc 'Fully
|
17
|
-
def
|
18
|
-
node(node_name).
|
16
|
+
desc 'Fully converge a node'
|
17
|
+
def converge(node_name)
|
18
|
+
node(node_name).converge(params)
|
19
19
|
end
|
20
20
|
|
21
|
-
desc '
|
22
|
-
def
|
23
|
-
node(node_name).
|
21
|
+
desc 'Apply a run_list'
|
22
|
+
def apply(node_name, run_list)
|
23
|
+
node(node_name).apply(params, run_list.split(","))
|
24
24
|
end
|
25
25
|
|
26
26
|
desc 'SSH into a node'
|
@@ -30,25 +30,26 @@ class Gusteau::CLI < Optitron::CLI
|
|
30
30
|
|
31
31
|
desc 'Generate an SSH config'
|
32
32
|
def ssh_config
|
33
|
-
puts Gusteau::SSHConfig.new
|
33
|
+
puts Gusteau::SSHConfig.new(nodes)
|
34
34
|
end
|
35
35
|
|
36
36
|
desc 'Generate an example project (a bureau)'
|
37
37
|
def init(bureau_name)
|
38
|
-
Gusteau::Bureau.new(bureau_name)
|
38
|
+
Gusteau::Bureau.new(bureau_name).generate!
|
39
39
|
end
|
40
40
|
|
41
41
|
private
|
42
42
|
|
43
43
|
def node(node_name)
|
44
|
-
unless
|
45
|
-
abort "Node '#{node_name}' is unknown. Known nodes are #{
|
44
|
+
unless node = nodes[node_name]
|
45
|
+
abort "Node '#{node_name}' is unknown. Known nodes are #{nodes.keys.join(', ')}."
|
46
|
+
else
|
47
|
+
node
|
46
48
|
end
|
47
|
-
Gusteau::Node.new(node_path)
|
48
49
|
end
|
49
50
|
|
50
|
-
def
|
51
|
-
|
51
|
+
def nodes
|
52
|
+
@nodes ||= Gusteau::Config.nodes(".gusteau.yml")
|
52
53
|
end
|
53
54
|
|
54
55
|
end
|
data/gusteau.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.version = Gusteau::VERSION
|
9
9
|
gem.authors = ["Vasily Mikhaylichenko", "Chris"]
|
10
10
|
gem.email = ["vasily@locomote.com", "chris@locomote.com"]
|
11
|
-
gem.description = %q{
|
11
|
+
gem.description = %q{Chef Solo wrapper and configuration manager}
|
12
12
|
gem.summary = %q{Making servers provisioning enjoyable since 2013.}
|
13
13
|
gem.homepage = "http://gusteau.gs"
|
14
14
|
|
@@ -21,9 +21,12 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.add_dependency 'inform'
|
22
22
|
gem.add_dependency 'json'
|
23
23
|
gem.add_dependency 'hashie'
|
24
|
+
gem.add_dependency 'hash-deep-merge'
|
24
25
|
gem.add_dependency 'net-ssh', '>= 2.2.2'
|
25
26
|
gem.add_dependency 'archive-tar-minitar', '>= 0.5.2'
|
26
27
|
|
27
28
|
gem.add_development_dependency 'minitest'
|
28
29
|
gem.add_development_dependency 'mocha'
|
30
|
+
gem.add_development_dependency 'simplecov'
|
31
|
+
gem.add_development_dependency 'coveralls'
|
29
32
|
end
|
data/lib/gusteau.rb
CHANGED
data/lib/gusteau/bureau.rb
CHANGED
@@ -8,41 +8,53 @@ module Gusteau
|
|
8
8
|
include Gusteau::ERB
|
9
9
|
|
10
10
|
def initialize(name)
|
11
|
-
|
11
|
+
@name = name
|
12
|
+
@template_path = File.expand_path('../../../template', __FILE__)
|
12
13
|
|
13
14
|
@login = Etc.getlogin
|
14
15
|
@ssh_key = File.read(File.expand_path '~/.ssh/id_rsa.pub').chomp rescue 'Your SSH key'
|
15
16
|
|
16
17
|
abort "Directory #{name} already exists" if Dir.exists?(name)
|
18
|
+
end
|
17
19
|
|
18
|
-
|
20
|
+
def generate!(init = true)
|
21
|
+
FileUtils.cp_r(@template_path, @name)
|
22
|
+
yaml_template '.gusteau.yml'
|
23
|
+
text_template 'README.md'
|
24
|
+
json_template "data_bags/users/#{@login}.json", "data_bags/users/user.json.erb"
|
25
|
+
Dir.chdir(name) { system "bash ./init.sh ; rm ./init.sh" } if(init)
|
26
|
+
end
|
19
27
|
|
20
|
-
|
21
|
-
read_erb_yaml(File.join(template_path, 'nodes', 'example.yml.erb')).tap do |node|
|
22
|
-
f.write node.to_yaml
|
23
|
-
f.close
|
24
|
-
end
|
28
|
+
private
|
25
29
|
|
26
|
-
|
30
|
+
def yaml_template(file)
|
31
|
+
replace_template file do |f|
|
32
|
+
read_erb_yaml("#{@template_path}/#{file}.erb").tap { |c| f.write(c.to_yaml) }
|
27
33
|
end
|
34
|
+
end
|
28
35
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
def json_template(file, src)
|
37
|
+
replace_template file, src do |f|
|
38
|
+
read_erb_json("#{@template_path}/#{src}").tap { |c| f.write JSON::pretty_generate(c) }
|
39
|
+
end
|
40
|
+
end
|
34
41
|
|
35
|
-
|
42
|
+
def text_template(file)
|
43
|
+
replace_template file do |f|
|
44
|
+
read_erb("#{@template_path}/#{file}.erb").tap { |t| f.write t }
|
36
45
|
end
|
46
|
+
end
|
37
47
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
system 'bundle'
|
48
|
+
def replace_template(file, src = nil)
|
49
|
+
dest = "#{@name}/#{file}"
|
50
|
+
src = "#{@name}/#{src}" if(src)
|
42
51
|
|
43
|
-
|
44
|
-
|
52
|
+
File.open(dest, 'w+') do |f|
|
53
|
+
yield f
|
54
|
+
f.close
|
55
|
+
FileUtils.rm(src || "#{dest}.erb")
|
45
56
|
end
|
46
57
|
end
|
58
|
+
|
47
59
|
end
|
48
60
|
end
|
data/lib/gusteau/chef.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'gusteau/erb'
|
2
|
+
require 'hash_deep_merge'
|
3
|
+
|
4
|
+
module Gusteau
|
5
|
+
module Config
|
6
|
+
extend self
|
7
|
+
extend Gusteau::ERB
|
8
|
+
|
9
|
+
def nodes(config_path)
|
10
|
+
if File.exists?(config_path)
|
11
|
+
env_config = read_erb_yaml(config_path)['environments']
|
12
|
+
env_config.inject({}) do |nodes, (env_name, env_hash)|
|
13
|
+
if env_hash['nodes']
|
14
|
+
env_hash['nodes'].each_pair do |node_name, node_hash|
|
15
|
+
node_name = "#{env_name}-#{node_name}"
|
16
|
+
nodes[node_name] = build_node(node_name, env_hash, node_hash)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
nodes
|
20
|
+
end
|
21
|
+
else
|
22
|
+
abort ".gusteau.yml not found"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Node attributes get deep-merged with the environment ones
|
28
|
+
# Node run_list overrides the environment one
|
29
|
+
#
|
30
|
+
def build_node(node_name, env_hash, node_hash)
|
31
|
+
config = {
|
32
|
+
'server' => node_hash,
|
33
|
+
'attributes' => env_hash['attributes'].deep_merge(node_hash['attributes'] || {}),
|
34
|
+
'run_list' => node_hash['run_list'] || env_hash['run_list']
|
35
|
+
}
|
36
|
+
Gusteau::Node.new(node_name, config)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/gusteau/node.rb
CHANGED
@@ -1,75 +1,43 @@
|
|
1
1
|
require 'gusteau/server'
|
2
2
|
require 'gusteau/chef'
|
3
|
-
require 'gusteau/erb'
|
4
3
|
|
5
4
|
module Gusteau
|
6
5
|
class Node
|
7
|
-
include Gusteau::ERB
|
8
|
-
|
9
6
|
attr_reader :name, :config, :server
|
10
7
|
|
11
|
-
def initialize(
|
12
|
-
|
13
|
-
|
14
|
-
@name = File.basename(path).gsub('.yml','')
|
15
|
-
@config = read_erb_yaml(path)
|
8
|
+
def initialize(name, config)
|
9
|
+
@name = name
|
10
|
+
@config = config
|
16
11
|
|
17
12
|
@server = Server.new(@config['server']) if @config['server']
|
18
13
|
@dna_path = '/tmp/dna.json'
|
19
14
|
end
|
20
15
|
|
21
|
-
def
|
22
|
-
|
23
|
-
server.chef.run opts, dna(true)
|
24
|
-
end
|
16
|
+
def converge(opts = {})
|
17
|
+
server.chef.run opts, dna
|
25
18
|
end
|
26
19
|
|
27
|
-
def
|
28
|
-
|
29
|
-
server.chef.run opts, dna(false, recipes.flatten)
|
30
|
-
end
|
20
|
+
def apply(opts = {}, run_list)
|
21
|
+
server.chef.run opts, dna(run_list)
|
31
22
|
end
|
32
23
|
|
33
24
|
def ssh
|
34
|
-
|
35
|
-
server.ssh
|
36
|
-
end
|
25
|
+
server.ssh
|
37
26
|
end
|
38
27
|
|
39
28
|
private
|
40
29
|
|
41
|
-
def dna(
|
30
|
+
def dna(run_list = nil)
|
42
31
|
node_dna = {
|
43
32
|
:path => @dna_path,
|
44
33
|
:hash => {
|
45
34
|
:instance_role => @name,
|
46
|
-
:run_list => run_list
|
47
|
-
}.merge(@config['
|
35
|
+
:run_list => run_list || @config['run_list']
|
36
|
+
}.merge(@config['attributes'] || {})
|
48
37
|
}
|
49
38
|
|
50
39
|
File.open(node_dna[:path], 'w+') { |f| f.puts node_dna[:hash].to_json }
|
51
40
|
node_dna
|
52
41
|
end
|
53
|
-
|
54
|
-
def run_list(include_all, recipes)
|
55
|
-
if include_all
|
56
|
-
list = []
|
57
|
-
list += @config['roles'].map { |r| "role[#{r}]" } if @config['roles']
|
58
|
-
list += @config['recipes'].map { |r| "recipe[#{r}]" } if @config['recipes']
|
59
|
-
list
|
60
|
-
else
|
61
|
-
recipes.map { |r| "recipe[#{r}]" }
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def wrap_vagrant(method)
|
66
|
-
if server
|
67
|
-
yield
|
68
|
-
elsif @config['vagrant']
|
69
|
-
Vagrant.send(method, @name)
|
70
|
-
else
|
71
|
-
Kernel.abort "Neither 'server' nor 'vagrant' defined for #{@name}. Please provide one."
|
72
|
-
end
|
73
|
-
end
|
74
42
|
end
|
75
43
|
end
|
data/lib/gusteau/ssh_config.rb
CHANGED
@@ -3,13 +3,14 @@ require 'gusteau/server'
|
|
3
3
|
|
4
4
|
module Gusteau
|
5
5
|
class SSHConfig
|
6
|
-
def initialize(
|
7
|
-
@config =
|
8
|
-
name = File.basename(n, '.*')
|
9
|
-
config = YAML::load_file(n)['server']
|
6
|
+
def initialize(nodes)
|
7
|
+
@config = []
|
10
8
|
|
11
|
-
|
12
|
-
|
9
|
+
nodes.each_pair do |name, node|
|
10
|
+
if server = node.server
|
11
|
+
@config << section(name, server)
|
12
|
+
end
|
13
|
+
end
|
13
14
|
end
|
14
15
|
|
15
16
|
def section(name, server)
|
@@ -25,7 +26,7 @@ Host #{name}
|
|
25
26
|
<<-eos
|
26
27
|
# BEGIN GUSTEAU NODES
|
27
28
|
|
28
|
-
#{@config}
|
29
|
+
#{@config.join("\n")}
|
29
30
|
# END GUSTEAU NODES
|
30
31
|
eos
|
31
32
|
end
|