masterbaker 0.7.8
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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +3 -0
- data/LICENSE +44 -0
- data/README.md +118 -0
- data/bin/bake +4 -0
- data/lib/masterbaker.rb +2 -0
- data/lib/masterbaker/cli.rb +178 -0
- data/lib/masterbaker/config.rb +113 -0
- data/lib/masterbaker/remote.rb +66 -0
- data/lib/masterbaker/remote_config.rb +62 -0
- data/lib/masterbaker/royal_crown.rb +79 -0
- data/lib/masterbaker/version.rb +3 -0
- data/masterbaker.gemspec +43 -0
- metadata +240 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: d5c68e02c977b9c560db370a7b8affae5fae2701
|
|
4
|
+
data.tar.gz: 2b531229a74c94510fb329f21d55e1020f520019
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 67ad3b16e650e2bd460bbf310aa8bec625e708c05ad8af4036463227a8fd6bc6cc103f361c49be2962a0cca06255bf93bfb087533fb38c7fb94567847892a30a
|
|
7
|
+
data.tar.gz: 059a748b3380cb2b7a8aa426660c0a3cb2e964e7719020c98950de6cf3f0f9eaa0053fa29c343971476b2d016a576792f29ba807fd108e0b2225146230bc242d
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Copyright (c) 2014 Mike Mackintosh
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
# a copy of this software and associated documentation files (the
|
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
# the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be
|
|
12
|
+
# included in all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
21
|
+
#
|
|
22
|
+
#
|
|
23
|
+
# ORIGINAL LICENSE
|
|
24
|
+
#
|
|
25
|
+
# Copyright (c) 2012 Matthew Kocher and Doc Ritezel
|
|
26
|
+
#
|
|
27
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
28
|
+
# a copy of this software and associated documentation files (the
|
|
29
|
+
# "Software"), to deal in the Software without restriction, including
|
|
30
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
31
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
32
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
33
|
+
# the following conditions:
|
|
34
|
+
#
|
|
35
|
+
# The above copyright notice and this permission notice shall be
|
|
36
|
+
# included in all copies or substantial portions of the Software.
|
|
37
|
+
#
|
|
38
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
39
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
40
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
41
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
42
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
43
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
44
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Bakist
|
|
2
|
+
|
|
3
|
+
Bakist spawned from the [Soloist](https://github.com/mkocher/soloist) project, and lets you quickly and easily converge [Chef](http://opscode.com/chef) recipes using [chef-solo](http://wiki.opscode.com/display/chef/Chef+Solo). It does not require a Chef server, but can exploit [community cookbooks](http://community.opscode.com/cookbooks), github-hosted cookbooks and locally-sourced cookbooks through [Librarian](https://github.com/applicationsonline/librarian).
|
|
4
|
+
|
|
5
|
+
Bakist was designed with users in mind, allowing a global runlist as well as user-specific.
|
|
6
|
+
|
|
7
|
+
Using Bakist
|
|
8
|
+
-------------
|
|
9
|
+
|
|
10
|
+
1. You'll need to have Bakist installed:
|
|
11
|
+
|
|
12
|
+
$ gem install bakist
|
|
13
|
+
|
|
14
|
+
2. You'll need a `Cheffile` in your home directory that points Librarian to all the cookbooks you've included:
|
|
15
|
+
|
|
16
|
+
$ cat ~/.bakery/Cheffile // or ~/.bakist/bakistrc
|
|
17
|
+
site "http://community.opscode.com/api/v1"
|
|
18
|
+
cookbook "pivotal_workstation",
|
|
19
|
+
:git => "https://github.com/pivotal/pivotal_workstation"
|
|
20
|
+
|
|
21
|
+
3. You'll need to create a `~/.bakery/bakeryrc` or `~/.bakist/bakistrc` file in your home directory to tell Chef which recipes to converge:
|
|
22
|
+
|
|
23
|
+
$ cat /Users/pivotal/.bakery/bakeryrc
|
|
24
|
+
recipes:
|
|
25
|
+
- pivotal_workstation::default
|
|
26
|
+
- pivotal_workstation::sublime_text
|
|
27
|
+
|
|
28
|
+
4. It's always a good idea to see what is found and will be installed on next run using `config`:
|
|
29
|
+
|
|
30
|
+
$ bake config
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
4. You'll need to run `bakist` for anything to happen:
|
|
34
|
+
|
|
35
|
+
$ bake
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Examples
|
|
39
|
+
--------
|
|
40
|
+
|
|
41
|
+
##### Running a set of recipes
|
|
42
|
+
|
|
43
|
+
Here's an example of a `~/.bakery/bakeryrc` or `~/.bakist/bakistrc`:
|
|
44
|
+
|
|
45
|
+
cookbook_paths:
|
|
46
|
+
- /opt/beans
|
|
47
|
+
recipes:
|
|
48
|
+
- beans::chili
|
|
49
|
+
- beans::food_fight
|
|
50
|
+
- napkins
|
|
51
|
+
|
|
52
|
+
This tells Bakist to search in both `/opt/beans` and `./cookbooks` (relative to the `~/.bakery` or `~/.bakist` directories) for cookbooks to run. Then, it attempts to converge the `beans::chili`, `beans::food_fight` and `napkins` recipes.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
##### Setting node attributes
|
|
56
|
+
|
|
57
|
+
Bakist lets you override node attributes. Let's say we've got a `bash::prompt` recipe for which we want to set `node['bash']['prompt']['color']='p!nk'`. No problem!
|
|
58
|
+
|
|
59
|
+
recipes:
|
|
60
|
+
- bash::prompt
|
|
61
|
+
node_attributes:
|
|
62
|
+
bash:
|
|
63
|
+
prompt:
|
|
64
|
+
color: p!nk
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
##### Conditionally modifying Bakist
|
|
68
|
+
|
|
69
|
+
Bakist allows conditionally switching on environment variables. Let's say we only want to include the `embarrassment::parental` recipe when the `MEGA_PRODUCTION` environment variable is set to `juggalos`. Here's the `~/.bakery/bakeryrc` or `~/.bakist/bakistrc`:
|
|
70
|
+
|
|
71
|
+
cookbook_paths:
|
|
72
|
+
- /fresno
|
|
73
|
+
recipes:
|
|
74
|
+
- disaster
|
|
75
|
+
env_variable_switches:
|
|
76
|
+
MEGA_PRODUCTION:
|
|
77
|
+
juggalos:
|
|
78
|
+
recipes:
|
|
79
|
+
- embarrassment::parental
|
|
80
|
+
|
|
81
|
+
So now, this is the result of our Bakist run:
|
|
82
|
+
|
|
83
|
+
$ MEGA_PRODUCTION=juggalos bake
|
|
84
|
+
Installing disaster (1.0.0)
|
|
85
|
+
Installing embarrassment (1.0.0)
|
|
86
|
+
… chef output …
|
|
87
|
+
INFO: Run List expands to [disaster, embarrassment::parental, faygo]
|
|
88
|
+
… chef output …
|
|
89
|
+
|
|
90
|
+
If we set `MEGA_PRODUCTION=godspeed`, the `embarrassment::parental` recipe is not converged.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
##### Running one-off recipes
|
|
94
|
+
|
|
95
|
+
Bakist can also run one-off recipes:
|
|
96
|
+
|
|
97
|
+
$ bake run_recipe lice::box
|
|
98
|
+
Installing lice (1.0.0)
|
|
99
|
+
… chef output …
|
|
100
|
+
INFO: Run List expands to [lice::box]
|
|
101
|
+
… chef output …
|
|
102
|
+
|
|
103
|
+
This just runs the `lice::box` recipe from your current set of cookbooks. It still uses all the `node_attributes` and `env_variable_switches` logic.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
##### Chef logging
|
|
107
|
+
|
|
108
|
+
Bakist runs `chef-solo` at log level `info` by default, which is helpful when you need to see what your Chef run is doing. If you need more information, you can set the `LOG_LEVEL` environment variable:
|
|
109
|
+
|
|
110
|
+
$ LOG_LEVEL=debug bake
|
|
111
|
+
|
|
112
|
+
The default log level is `FATAL` to prevent undesireable output.
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
License
|
|
116
|
+
=======
|
|
117
|
+
|
|
118
|
+
See LICENSE for details.
|
data/bin/bake
ADDED
data/lib/masterbaker.rb
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
require "librarian/chef/cli"
|
|
2
|
+
require "masterbaker/remote_config"
|
|
3
|
+
require "awesome_print"
|
|
4
|
+
require "thor"
|
|
5
|
+
|
|
6
|
+
class Thor::Shell::Color
|
|
7
|
+
def say(string, color=:WHITE, nl=true, prefix=true)
|
|
8
|
+
|
|
9
|
+
if prefix
|
|
10
|
+
#super '★', :green, false
|
|
11
|
+
super ' Masterbaker ', :white, false
|
|
12
|
+
#super '★', :green, false
|
|
13
|
+
super ':: ', :white, false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
super(string, color, nl)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module Masterbaker
|
|
21
|
+
|
|
22
|
+
class CLI < Thor
|
|
23
|
+
attr_writer :masterbaker_config
|
|
24
|
+
default_task :chef
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
desc "chef", "Run chef-solo"
|
|
28
|
+
method_option :remote, :aliases => "-r", :desc => "Run chef-solo on user@host"
|
|
29
|
+
method_option :identity, :aliases => "-i", :desc => "The SSH identity file"
|
|
30
|
+
def chef
|
|
31
|
+
#begin
|
|
32
|
+
say 'Masterbaker is Fetching Cookbooks', :green, true, false
|
|
33
|
+
install_cookbooks if cheffile_exists?
|
|
34
|
+
say 'Masterbaker is Preparing to Bake', :green, true, false
|
|
35
|
+
masterbaker_config.run_chef
|
|
36
|
+
#rescue Exception => msg
|
|
37
|
+
# say msg.backtrace.inspect
|
|
38
|
+
# say ' Your Bakery files were not found. Please make sure ', :RED, false
|
|
39
|
+
# say '~/.bakery/', :YELLOW, false, false
|
|
40
|
+
# say ' exists', :RED, true, false
|
|
41
|
+
# say '', nil, nil, false
|
|
42
|
+
#end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc "run_recipe [cookbook::recipe, ...]", "Run individual recipes"
|
|
46
|
+
method_option :remote, :aliases => "-r", :desc => "Run recipes on user@host"
|
|
47
|
+
method_option :identity, :aliases => "-i", :desc => "The SSH identity file"
|
|
48
|
+
def run_recipe(*recipes)
|
|
49
|
+
masterbaker_config.royal_crown.recipes = recipes
|
|
50
|
+
chef
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
desc "config", "Dumps configuration data for Masterbaker"
|
|
54
|
+
def config
|
|
55
|
+
Kernel.ap(masterbaker_config.as_node_json)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
no_tasks do
|
|
59
|
+
def install_cookbooks
|
|
60
|
+
|
|
61
|
+
Dir.chdir(File.dirname(config_path)) do
|
|
62
|
+
Librarian::Chef::Cli.with_environment do
|
|
63
|
+
Librarian::Chef::Cli.new.install
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
if user_config_path
|
|
68
|
+
say 'Masterbaker is Fetching User-Specific Cookbooks', :green, true, false
|
|
69
|
+
begin
|
|
70
|
+
Dir.chdir(File.dirname(user_config_path)) do
|
|
71
|
+
Librarian::Chef::Cli.with_environment do
|
|
72
|
+
libchef = Librarian::Chef::Cli.new.install
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
rescue
|
|
76
|
+
say 'Error getting user-specific cookbooks, scratching.', :red
|
|
77
|
+
abort
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def masterbaker_config
|
|
84
|
+
@masterbaker_config ||= if options[:remote]
|
|
85
|
+
Masterbaker::RemoteConfig.from_file(config_path, remote)
|
|
86
|
+
else
|
|
87
|
+
Masterbaker::Config.from_file(config_path)
|
|
88
|
+
end.tap do |config|
|
|
89
|
+
config.merge!(user_config) if user_config_path
|
|
90
|
+
config.merge!(shop_config) if shop_config_path
|
|
91
|
+
#config.merge!(shop_managed_user_config) if shop_managed_user_config_path
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
def shop_config
|
|
98
|
+
Masterbaker::Config.from_file(shop_config_path)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def user_config
|
|
102
|
+
Masterbaker::Config.from_file(user_config_path)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def shop_managed_user_config
|
|
106
|
+
Masterbaker::Config.from_file(shop_managed_user_config_path)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def remote
|
|
110
|
+
@remote ||= if options[:identity]
|
|
111
|
+
Masterbaker::Remote.from_uri(options[:remote], options[:identity])
|
|
112
|
+
else
|
|
113
|
+
Masterbaker::Remote.from_uri(options[:remote])
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def cheffile_exists?
|
|
118
|
+
File.exists?(File.expand_path("~/.bakery/Cheffile"))
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def config_path
|
|
122
|
+
@config_path ||= File.expand_path("~/.bakery/bakeryrc")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# TODO: Fix librarian cookbook path defaults
|
|
126
|
+
def shop_managed_user_config_path
|
|
127
|
+
# current_user = ENV['SUDO_USER'] ||= ENV['USER']
|
|
128
|
+
# shop_managed_user_config_path = File.expand_path("~/.bakery/bakers/#{current_user}/bakeryrc")
|
|
129
|
+
# if File.exists?(shop_managed_user_config_path)
|
|
130
|
+
# @shop_managed_user_config_path ||= shop_managed_user_config_path
|
|
131
|
+
# end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def user_config_path
|
|
135
|
+
user_config_path = File.expand_path("~/.masterbaker/masterbakerrc")
|
|
136
|
+
if File.exists?(user_config_path)
|
|
137
|
+
@user_config_path ||= user_config_path
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def shop_config_path
|
|
142
|
+
shop_config_path = File.expand_path("~/.bakery/shoprc")
|
|
143
|
+
if File.exists?(shop_config_path)
|
|
144
|
+
@shop_config_path ||= shop_config_path
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def logo
|
|
149
|
+
say ' ', :green, true, false
|
|
150
|
+
say ' kkkkk ', :green, true, false
|
|
151
|
+
say ' kbbbaaaaaaaabk kkkkk ', :green, true, false
|
|
152
|
+
say ' kaabkbaaabkbaaaak baaaak ', :green, true, false
|
|
153
|
+
say ' kaaak kaaaab kaabb kaaaab ', :green, true, false
|
|
154
|
+
say ' kbaab kbaaaak kbaab kkbbbkkbbbb baaaak kbbk kkbbbbk kbbbkkkkbbk kbbbk kbbbbk ', :green, true, false
|
|
155
|
+
say ' kabab kaaaaabaaaab kaaaaaaaaaaab kaaaaa kbaabk baaabbaaakkbaaaaaaaaaabaaaak kbaaabk ', :green, true, false
|
|
156
|
+
say ' k kbbkkbaaaabaaaaaaak baaaak kbaaaak baaaabbaabk kbaaab kbaakkaaaabkbaabbaaaaa baaaab k ', :green, true, false
|
|
157
|
+
say ' kbaabk kaaaab kbaaaaakbaaaak kaaaaak kbaaaaaaaaaaak kbaaaakkbaakkbaaaak kkkbaaaak kbaaaak kbaabk ', :green, true, false
|
|
158
|
+
say ' kaaab baaaak kbaaaakaaaab baaaab kaaaabkbaaaaab baaaaababk kaaaab kaaaaa kaaaab baaak ', :green, true, false
|
|
159
|
+
say ' k kk kaaaab kaaaabbaaaak kaaaaa kbaaaak baaaak kaaaaakkk kbaaaak kaaaak baaaak kk k ', :green, true, false
|
|
160
|
+
say ' baaaabbkkkbaaabkaaaaaakbbaaaabkkbaaaab kaaaaabkbaaaaabkkkkbbaaaab baaaabbbaaaab ', :green, true, false
|
|
161
|
+
say ' kbaaaabaaaaaabk baaaaaaabaaaaaaaaaaaak kaaaaaaabaaaaaaaaabkbaaaak baaaaaabaaaak ', :green, true, false
|
|
162
|
+
say ' kbbbbkkbbaabk kbbbbk kbbbbkkbbbbk kbbbbbk kbbbbbkk bbbbk kbbbkkaaaaa ', :green, true, false
|
|
163
|
+
say ' kkkkkkbbbbbbbbbbbbbbbkkkkkkkk kbaaaak ', :green, true, false
|
|
164
|
+
say ' kkbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbkkk kkkbaaaabk ', :green, true, false
|
|
165
|
+
say ' kbbaabbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbaaaaaaaabk ', :green, true, false
|
|
166
|
+
say ' kkbaabbbbbaaaaaaaaaaaaaaaaaaaabbbbbbbbkkkkkbbbbbbbbbbaaaaaaaaaaaaaaaaaaabkk ', :green, true, false
|
|
167
|
+
say ' kbaaaabbbaaaaaaaaaaaaaaabbkkkk kkkbbbbbbbbbkkk ', :green, true, false
|
|
168
|
+
say ' kbbbbbaaaaaaaaaaaaaabbkk ', :green, true, false
|
|
169
|
+
say ' kkbbaaaaaabbkk ', :green, true, false
|
|
170
|
+
say ' kbaabk ', :green, true, false
|
|
171
|
+
say ' kk ', :green, true, false
|
|
172
|
+
say ' ', :green, true, false
|
|
173
|
+
say 'Preparing... ', :white, true
|
|
174
|
+
say ' ', :green, true, false
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require "masterbaker/royal_crown"
|
|
2
|
+
require "tempfile"
|
|
3
|
+
|
|
4
|
+
module Masterbaker
|
|
5
|
+
class Config
|
|
6
|
+
attr_writer :solo_rb_path, :node_json_path
|
|
7
|
+
attr_reader :royal_crown
|
|
8
|
+
|
|
9
|
+
def self.from_file(royal_crown_path)
|
|
10
|
+
rc = Masterbaker::RoyalCrown.from_file(royal_crown_path)
|
|
11
|
+
new(rc)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(royal_crown)
|
|
15
|
+
@royal_crown = royal_crown
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run_chef
|
|
19
|
+
exec(conditional_sudo("bash -c '#{chef_solo}'"))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def chef_solo
|
|
23
|
+
"chef-solo -c '#{solo_rb_path}' -l '#{log_level}'"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def as_solo_rb
|
|
27
|
+
<<-SOLO_RB
|
|
28
|
+
file_cache_path "#{chef_cache_path}"
|
|
29
|
+
cookbook_path #{cookbook_paths.inspect}
|
|
30
|
+
json_attribs "#{node_json_path}"
|
|
31
|
+
SOLO_RB
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def as_node_json
|
|
35
|
+
compiled.node_attributes.to_hash.merge({ "recipes" => compiled.recipes })
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def chef_cache_path
|
|
39
|
+
"/var/chef/cache".tap do |cache_path|
|
|
40
|
+
system(conditional_sudo("mkdir -p #{cache_path}")) \
|
|
41
|
+
unless File.directory?(cache_path)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def cookbook_paths
|
|
46
|
+
([royal_crown_cookbooks_directory] + compiled.cookbook_paths).map do |path|
|
|
47
|
+
File.expand_path(path, royal_crown_path)
|
|
48
|
+
end.uniq.select do |path|
|
|
49
|
+
File.directory?(path)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def solo_rb_path
|
|
54
|
+
@solo_rb_path ||= Tempfile.new(["solo", ".rb"]).tap do |file|
|
|
55
|
+
puts as_solo_rb if debug?
|
|
56
|
+
file.write(as_solo_rb)
|
|
57
|
+
file.close
|
|
58
|
+
end.path
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def node_json_path
|
|
62
|
+
@node_json_path ||= Tempfile.new(["node", ".json"]).tap do |file|
|
|
63
|
+
puts JSON.pretty_generate(as_node_json) if debug?
|
|
64
|
+
file.write(JSON.dump(as_node_json))
|
|
65
|
+
file.close
|
|
66
|
+
end.path
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def merge!(other)
|
|
70
|
+
royal_crown.recipes += other.royal_crown.recipes
|
|
71
|
+
royal_crown.cookbook_paths += other.royal_crown.cookbook_paths
|
|
72
|
+
royal_crown.node_attributes.merge!(other.royal_crown.node_attributes)
|
|
73
|
+
royal_crown.env_variable_switches.merge!(other.royal_crown.env_variable_switches)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def compiled
|
|
77
|
+
@compiled ||= royal_crown.dup.tap do |working_royal_crown|
|
|
78
|
+
while working_royal_crown["env_variable_switches"]
|
|
79
|
+
working_royal_crown.delete("env_variable_switches").each do |variable, switch|
|
|
80
|
+
switch.each do |value, inner|
|
|
81
|
+
working_royal_crown.merge!(inner) if ENV[variable] == value
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def log_level
|
|
89
|
+
ENV["LOG_LEVEL"] || "fatal"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def debug?
|
|
93
|
+
log_level == "debug"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
def conditional_sudo(command)
|
|
98
|
+
root? ? command : "sudo -E #{command}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def root?
|
|
102
|
+
Process.uid == 0
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def royal_crown_cookbooks_directory
|
|
106
|
+
File.expand_path("cookbooks", royal_crown_path)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def royal_crown_path
|
|
110
|
+
File.dirname(royal_crown.path)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require "net/ssh"
|
|
2
|
+
require "shellwords"
|
|
3
|
+
require "etc"
|
|
4
|
+
|
|
5
|
+
module Masterbaker
|
|
6
|
+
class RemoteError < RuntimeError; end
|
|
7
|
+
|
|
8
|
+
class Remote
|
|
9
|
+
attr_reader :user, :host, :key, :timeout, :stdout, :stderr, :exitstatus
|
|
10
|
+
attr_writer :connection
|
|
11
|
+
|
|
12
|
+
def self.from_uri(uri, key = "~/.ssh/id_rsa")
|
|
13
|
+
parsed = URI.parse("ssh://#{uri}")
|
|
14
|
+
new(parsed.user || Etc.getlogin, parsed.host, key)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(user, host, key, options = {})
|
|
18
|
+
@user = user
|
|
19
|
+
@host = host
|
|
20
|
+
@key = File.expand_path(key)
|
|
21
|
+
@timeout = options[:timeout] || 10000
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def backtick(command)
|
|
25
|
+
@stdout = ""
|
|
26
|
+
@stderr = ""
|
|
27
|
+
exec(command)
|
|
28
|
+
@stdout
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def system(command)
|
|
32
|
+
@stdout = STDOUT
|
|
33
|
+
@stderr = STDERR
|
|
34
|
+
exec(command)
|
|
35
|
+
exitstatus
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def system!(*command)
|
|
39
|
+
system(*command).tap do |status|
|
|
40
|
+
raise RemoteError.new("#{command.join(" ")} exited #{status}") unless status == 0
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def upload(from, to, opts = "--exclude .git")
|
|
45
|
+
Kernel.system("rsync -e 'ssh -i #{key}' -avz --delete #{from} #{user}@#{host}:#{to} #{opts}")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
def connection
|
|
50
|
+
@connection ||= Net::SSH.start(host, user, :keys => [key], :timeout => timeout)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def exec(*command)
|
|
54
|
+
connection.open_channel do |channel|
|
|
55
|
+
channel.exec(*command) do |stream, success|
|
|
56
|
+
raise RemoteError.new("Could not run #{command.join(" ")}") unless success
|
|
57
|
+
stream.on_data { |_, data| stdout << data }
|
|
58
|
+
stream.on_extended_data { |_, type, data| stderr << data }
|
|
59
|
+
stream.on_request("exit-status") { |_, data| @exitstatus = data.read_long }
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
connection.loop
|
|
63
|
+
@exitstatus ||= 0
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require "masterbaker/config"
|
|
2
|
+
require "masterbaker/remote"
|
|
3
|
+
|
|
4
|
+
module Masterbaker
|
|
5
|
+
class RemoteConfig < Config
|
|
6
|
+
attr_reader :remote
|
|
7
|
+
|
|
8
|
+
def self.from_file(royal_crown_path, remote)
|
|
9
|
+
rc = Masterbaker::RoyalCrown.from_file(royal_crown_path)
|
|
10
|
+
new(rc, remote)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(royal_crown, remote)
|
|
14
|
+
@royal_crown = royal_crown
|
|
15
|
+
@remote = remote
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run_chef
|
|
19
|
+
remote.system!(conditional_sudo(%(/bin/bash -lc "#{chef_solo}")))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def node_json_path
|
|
23
|
+
@node_json_path ||= File.expand_path("node.json", chef_config_path).tap do |path|
|
|
24
|
+
remote.system!(%(echo '#{JSON.dump(as_node_json)}' | #{conditional_sudo("tee #{path}")}))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def solo_rb_path
|
|
29
|
+
@solo_rb_path ||= File.expand_path("solo.rb", chef_config_path).tap do |path|
|
|
30
|
+
remote.system!(%(echo '#{as_solo_rb}' | #{conditional_sudo("tee #{path}")}))
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def chef_cache_path
|
|
35
|
+
@chef_cache_path ||= "/var/chef/cache".tap do |cache_path|
|
|
36
|
+
remote.system!(conditional_sudo("/bin/mkdir -m 777 -p #{cache_path}"))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def chef_config_path
|
|
41
|
+
@chef_config_path ||= "/etc/chef".tap do |path|
|
|
42
|
+
remote.system!(conditional_sudo("/bin/mkdir -m 777 -p #{path}"))
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def cookbook_paths
|
|
47
|
+
@cookbook_paths ||= ["/var/chef/cookbooks".tap do |remote_path|
|
|
48
|
+
remote.system!(conditional_sudo("/bin/mkdir -m 777 -p #{remote_path}"))
|
|
49
|
+
super.each { |path| remote.upload("#{path}/", remote_path) }
|
|
50
|
+
end]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
def conditional_sudo(command)
|
|
55
|
+
root? ? command : "/usr/bin/sudo -E #{command}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def root?
|
|
59
|
+
remote.user == "root"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require "hashie"
|
|
2
|
+
|
|
3
|
+
module Masterbaker
|
|
4
|
+
class RoyalCrown < Hashie::Trash
|
|
5
|
+
property :path
|
|
6
|
+
property :recipes, :default => []
|
|
7
|
+
property :cookbook_paths, :default => []
|
|
8
|
+
property :node_attributes, :default => Hashie::Mash.new,
|
|
9
|
+
:transform_with => lambda { |v| Hashie::Mash.new(v) }
|
|
10
|
+
property :env_variable_switches, :default => Hashie::Mash.new,
|
|
11
|
+
:transform_with => lambda { |v| Hashie::Mash.new(v) }
|
|
12
|
+
|
|
13
|
+
def node_attributes=(hash)
|
|
14
|
+
self["node_attributes"] = Hashie::Mash.new(hash)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def merge!(other_royal_crown)
|
|
18
|
+
merge_recipes(other_royal_crown["recipes"])
|
|
19
|
+
merge_cookbook_paths(other_royal_crown["cookbook_paths"])
|
|
20
|
+
self.node_attributes.deep_merge!(other_royal_crown["node_attributes"])
|
|
21
|
+
self.env_variable_switches = other_royal_crown["env_variable_switches"]
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def merge_recipes(new_recipes = [])
|
|
26
|
+
merge_array_property("recipes", new_recipes)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def merge_cookbook_paths(new_cookbook_paths = [])
|
|
30
|
+
merge_array_property("cookbook_paths", new_cookbook_paths)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def env_variable_switches=(hash)
|
|
34
|
+
self["env_variable_switches"] ||= Hashie::Mash.new
|
|
35
|
+
self["env_variable_switches"].merge!(Hashie::Mash.new(hash))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_yaml
|
|
39
|
+
to_hash.tap do |hash|
|
|
40
|
+
hash.delete("path")
|
|
41
|
+
self.class.nilable_properties.each { |k| hash[k] = nil if hash[k].empty? }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def save
|
|
46
|
+
return self unless path
|
|
47
|
+
File.open(path, "w+") { |file| file.write(YAML.dump(to_yaml)) }
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def reload
|
|
52
|
+
self.class.from_file(path)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.from_file(file_path)
|
|
56
|
+
new(read_config(file_path).merge("path" => file_path))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.read_config(yaml_file)
|
|
60
|
+
content = File.read(yaml_file)
|
|
61
|
+
YAML.load(ERB.new(content).result).tap do |hash|
|
|
62
|
+
nilable_properties.each do |key|
|
|
63
|
+
hash.delete(key) if hash[key].nil?
|
|
64
|
+
end if hash
|
|
65
|
+
end || {}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
def self.nilable_properties
|
|
70
|
+
(properties - [:path]).map(&:to_s)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def merge_array_property(property_name, values)
|
|
74
|
+
self[property_name] ||= []
|
|
75
|
+
self[property_name] += values
|
|
76
|
+
self[property_name].uniq!
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/masterbaker.gemspec
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
# From the lovely work of "Matthew Kocher", "Doc Ritezel"
|
|
3
|
+
# who can be found at "kocher@gmail.com", "ritezel@gmail.com"
|
|
4
|
+
#
|
|
5
|
+
# Ported from Soloist
|
|
6
|
+
#
|
|
7
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
8
|
+
require "masterbaker/version"
|
|
9
|
+
|
|
10
|
+
Gem::Specification.new do |s|
|
|
11
|
+
s.name = "masterbaker"
|
|
12
|
+
s.version = Masterbaker::VERSION
|
|
13
|
+
s.platform = Gem::Platform::RUBY
|
|
14
|
+
s.authors = ["Mike Mackintosh"]
|
|
15
|
+
s.email = ["mike.mackintosh@gmail.com"]
|
|
16
|
+
s.homepage = "http://github.com/mikemackintosh/masterbaker"
|
|
17
|
+
s.summary = "Masterbaker is a simple way of running chef-solo"
|
|
18
|
+
s.description = "Makes running chef-solo easy."
|
|
19
|
+
|
|
20
|
+
s.rubyforge_project = "masterbaker"
|
|
21
|
+
|
|
22
|
+
s.files = `git ls-files`.split("\n")
|
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
25
|
+
s.require_paths = ["lib"]
|
|
26
|
+
|
|
27
|
+
s.license = 'MIT'
|
|
28
|
+
|
|
29
|
+
s.add_dependency "chef"
|
|
30
|
+
s.add_dependency "librarian-chef"
|
|
31
|
+
s.add_dependency "thor"
|
|
32
|
+
s.add_dependency "hashie", "~> 2.0"
|
|
33
|
+
s.add_dependency "net-ssh"
|
|
34
|
+
s.add_dependency "awesome_print"
|
|
35
|
+
|
|
36
|
+
s.add_development_dependency "rspec"
|
|
37
|
+
s.add_development_dependency "guard-rspec"
|
|
38
|
+
s.add_development_dependency "guard-bundler"
|
|
39
|
+
s.add_development_dependency "guard-shell"
|
|
40
|
+
s.add_development_dependency "rb-fsevent"
|
|
41
|
+
s.add_development_dependency "terminal-notifier-guard"
|
|
42
|
+
s.add_development_dependency "gem-release"
|
|
43
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: masterbaker
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.7.8
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mike Mackintosh
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-11-01 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: chef
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '>='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '>='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: librarian-chef
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: thor
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - '>='
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - '>='
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: hashie
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ~>
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '2.0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ~>
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '2.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: net-ssh
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - '>='
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - '>='
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: awesome_print
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - '>='
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - '>='
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rspec
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - '>='
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - '>='
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: guard-rspec
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - '>='
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - '>='
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: guard-bundler
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - '>='
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - '>='
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: guard-shell
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - '>='
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - '>='
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: rb-fsevent
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - '>='
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: '0'
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - '>='
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: terminal-notifier-guard
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - '>='
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
174
|
+
type: :development
|
|
175
|
+
prerelease: false
|
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - '>='
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: '0'
|
|
181
|
+
- !ruby/object:Gem::Dependency
|
|
182
|
+
name: gem-release
|
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - '>='
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '0'
|
|
188
|
+
type: :development
|
|
189
|
+
prerelease: false
|
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
+
requirements:
|
|
192
|
+
- - '>='
|
|
193
|
+
- !ruby/object:Gem::Version
|
|
194
|
+
version: '0'
|
|
195
|
+
description: Makes running chef-solo easy.
|
|
196
|
+
email:
|
|
197
|
+
- mike.mackintosh@gmail.com
|
|
198
|
+
executables:
|
|
199
|
+
- bake
|
|
200
|
+
extensions: []
|
|
201
|
+
extra_rdoc_files: []
|
|
202
|
+
files:
|
|
203
|
+
- .gitignore
|
|
204
|
+
- Gemfile
|
|
205
|
+
- LICENSE
|
|
206
|
+
- README.md
|
|
207
|
+
- bin/bake
|
|
208
|
+
- lib/masterbaker.rb
|
|
209
|
+
- lib/masterbaker/cli.rb
|
|
210
|
+
- lib/masterbaker/config.rb
|
|
211
|
+
- lib/masterbaker/remote.rb
|
|
212
|
+
- lib/masterbaker/remote_config.rb
|
|
213
|
+
- lib/masterbaker/royal_crown.rb
|
|
214
|
+
- lib/masterbaker/version.rb
|
|
215
|
+
- masterbaker.gemspec
|
|
216
|
+
homepage: http://github.com/mikemackintosh/masterbaker
|
|
217
|
+
licenses:
|
|
218
|
+
- MIT
|
|
219
|
+
metadata: {}
|
|
220
|
+
post_install_message:
|
|
221
|
+
rdoc_options: []
|
|
222
|
+
require_paths:
|
|
223
|
+
- lib
|
|
224
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
225
|
+
requirements:
|
|
226
|
+
- - '>='
|
|
227
|
+
- !ruby/object:Gem::Version
|
|
228
|
+
version: '0'
|
|
229
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
230
|
+
requirements:
|
|
231
|
+
- - '>='
|
|
232
|
+
- !ruby/object:Gem::Version
|
|
233
|
+
version: '0'
|
|
234
|
+
requirements: []
|
|
235
|
+
rubyforge_project: masterbaker
|
|
236
|
+
rubygems_version: 2.0.14
|
|
237
|
+
signing_key:
|
|
238
|
+
specification_version: 4
|
|
239
|
+
summary: Masterbaker is a simple way of running chef-solo
|
|
240
|
+
test_files: []
|