gusteau 0.4.8 → 1.0.0.dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
[](https://www.travis-ci.org/locomote/gusteau)
|
7
|
+
[](https://coveralls.io/r/locomote/gusteau)
|
7
8
|
[](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
|