spiceweasel 0.7.1 → 0.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.
- data/README.md +3 -1
- data/bin/spiceweasel +23 -193
- data/lib/spiceweasel.rb +7 -1
- data/lib/spiceweasel/cli.rb +47 -0
- data/lib/spiceweasel/cookbook_list.rb +39 -0
- data/lib/spiceweasel/data_bag_list.rb +32 -0
- data/lib/spiceweasel/environment_list.rb +18 -0
- data/lib/spiceweasel/node_list.rb +38 -0
- data/lib/spiceweasel/role_list.rb +18 -0
- data/lib/spiceweasel/run_list.rb +36 -0
- data/lib/spiceweasel/version.rb +1 -1
- data/spec/bin/spiceweasel_spec.rb +39 -0
- metadata +60 -32
- data/.gitignore +0 -4
- data/CHANGELOG.md +0 -83
- data/Gemfile +0 -4
- data/Rakefile +0 -2
- data/example.json +0 -73
- data/example.yml +0 -41
- data/spiceweasel.gemspec +0 -25
- data/test.sh +0 -48
data/README.md
CHANGED
@@ -2,6 +2,8 @@ Description
|
|
2
2
|
===========
|
3
3
|
Spiceweasel is a command-line tool for batch loading Chef infrastructure. It provides a simple syntax in either JSON or YAML for describing and deploying infrastructure in order with the Chef command-line tool `knife`.
|
4
4
|
|
5
|
+
The `examples` directory provides examples based on the Quick Starts provided at [http://help.opscode.com/kb/otherhelp](http://help.opscode.com/kb/otherhelp).
|
6
|
+
|
5
7
|
CHANGELOG.md covers current, previous and future development milestones and contains the features backlog.
|
6
8
|
|
7
9
|
Requirements
|
@@ -12,7 +14,7 @@ Written with Chef 0.9.12 and 0.10.0 and supports cookbooks, environments, roles,
|
|
12
14
|
|
13
15
|
Testing
|
14
16
|
-------
|
15
|
-
Tested with Ubuntu 10.04 and
|
17
|
+
Tested with Ubuntu 10.04 and Chef 0.9.16 and 0.10.2.
|
16
18
|
|
17
19
|
File Syntax
|
18
20
|
===========
|
data/bin/spiceweasel
CHANGED
@@ -1,92 +1,17 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'json'
|
4
|
-
require 'mixlib/cli'
|
5
|
-
require 'spiceweasel/version'
|
6
4
|
require 'yaml'
|
7
5
|
|
8
|
-
|
9
|
-
def validate_run_list(run_list, cookbooks, environments, roles)
|
10
|
-
run_list.each do |item|
|
11
|
-
if item.start_with?("recipe")
|
12
|
-
#recipe[foo] or recipe[foo::bar]
|
13
|
-
recipe = item.slice(7..-2).split("::")[0]
|
14
|
-
unless cookbooks.member?(recipe)
|
15
|
-
raise "'#{item}' is an invalid run_list recipe not managed by spiceweasel"
|
16
|
-
exit(-1)
|
17
|
-
end
|
18
|
-
elsif item.start_with?("environment")
|
19
|
-
#environment[blah]
|
20
|
-
environment = item.slice(12..-2)
|
21
|
-
unless environments.member?(environment)
|
22
|
-
raise "'#{item}' is an invalid run_list environment not managed by spiceweasel"
|
23
|
-
exit(-1)
|
24
|
-
end
|
25
|
-
elsif item.start_with?("role")
|
26
|
-
#role[blah]
|
27
|
-
role = item.slice(5..-2)
|
28
|
-
unless roles.member?(role)
|
29
|
-
raise "'#{item}' is an invalid run_list role not managed by spiceweasel"
|
30
|
-
exit(-1)
|
31
|
-
end
|
32
|
-
else
|
33
|
-
raise "'#{item}' is an invalid run_list component"
|
34
|
-
exit(-1)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class SpiceweaselCLI
|
40
|
-
include Mixlib::CLI
|
41
|
-
|
42
|
-
banner("Usage: spiceweasel [option] file")
|
43
|
-
|
44
|
-
option :debug,
|
45
|
-
:long => "--debug",
|
46
|
-
:description => "Verbose debugging messages.",
|
47
|
-
:boolean => true
|
48
|
-
|
49
|
-
option :delete,
|
50
|
-
:short => "-d",
|
51
|
-
:long => "--delete",
|
52
|
-
:description => "Print the knife commands to be delete the infrastructure",
|
53
|
-
:boolean => true
|
54
|
-
|
55
|
-
option :dryrun,
|
56
|
-
:long => "--dryrun",
|
57
|
-
:description => "Print the knife commands to be executed to STDOUT",
|
58
|
-
:boolean => true
|
59
|
-
|
60
|
-
option :help,
|
61
|
-
:short => "-h",
|
62
|
-
:long => "--help",
|
63
|
-
:description => "Show this message",
|
64
|
-
:on => :tail,
|
65
|
-
:boolean => true,
|
66
|
-
:show_options => true,
|
67
|
-
:exit => 0
|
68
|
-
|
69
|
-
option :rebuild,
|
70
|
-
:short => "-r",
|
71
|
-
:long => "--rebuild",
|
72
|
-
:description => "Print the knife commands to be delete and recreate the infrastructure",
|
73
|
-
:boolean => true
|
74
|
-
|
75
|
-
option :version,
|
76
|
-
:short => "-v",
|
77
|
-
:long => "--version",
|
78
|
-
:description => "Version",
|
79
|
-
:boolean => true,
|
80
|
-
:proc => lambda {|v| puts "Spiceweasel: #{Spiceweasel::VERSION}" },
|
81
|
-
:exit => 0
|
82
|
-
end
|
6
|
+
require 'spiceweasel'
|
83
7
|
|
84
8
|
#process command line options
|
85
9
|
begin
|
86
|
-
ARGV << "-h" if ARGV.empty?
|
87
|
-
cli =
|
10
|
+
ARGV << "-h" if ARGV.empty?
|
11
|
+
cli = Spiceweasel::CLI.new
|
88
12
|
cli.parse_options
|
89
|
-
|
13
|
+
DEBUG = cli.config[:debug]
|
14
|
+
rescue OptionParser::InvalidOption => e
|
90
15
|
STDERR.puts e.message
|
91
16
|
puts cli.opt_parser.to_s
|
92
17
|
exit(-1)
|
@@ -94,7 +19,7 @@ end
|
|
94
19
|
|
95
20
|
begin
|
96
21
|
file = ARGV.last
|
97
|
-
STDOUT.puts "DEBUG: file: #{file}" if cli.config[:debug]
|
22
|
+
STDOUT.puts "DEBUG: file: #{file}" if cli.config[:debug]
|
98
23
|
if (file.end_with?(".yml"))
|
99
24
|
input = YAML.load_file ARGV.last
|
100
25
|
elsif (file.end_with?(".json"))
|
@@ -116,130 +41,35 @@ end
|
|
116
41
|
create = String.new()
|
117
42
|
delete = String.new()
|
118
43
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
cb = cookbook.keys.first
|
124
|
-
if cookbook[cb] and cookbook[cb].length > 0
|
125
|
-
version = cookbook[cb][0].to_s || ""
|
126
|
-
args = cookbook[cb][1] || ""
|
127
|
-
end
|
128
|
-
STDOUT.puts "DEBUG: cookbook: #{cb} #{version}" if cli.config[:debug]
|
129
|
-
delete += "knife cookbook delete #{cb} #{version} -y\n"
|
130
|
-
if File.directory?("cookbooks")
|
131
|
-
if version and File.directory?("cookbooks/#{cb}")
|
132
|
-
#check metadata.rb for requested version
|
133
|
-
metadata = File.open("cookbooks/#{cb}/metadata.rb").grep(/^version/)[0].split()[1].gsub(/"/,'').to_s
|
134
|
-
if (metadata != version)
|
135
|
-
raise "Invalid version #{version} of '#{cb}' requested, #{metadata} is already in the cookbooks directory."
|
136
|
-
exit(-1)
|
137
|
-
end
|
138
|
-
elsif !File.directory?("cookbooks/#{cb}")
|
139
|
-
create += "knife cookbook site download #{cb} #{version} --file cookbooks/#{cb}.tgz #{args}\n"
|
140
|
-
create += "tar -C cookbooks/ -xf cookbooks/#{cb}.tgz\n"
|
141
|
-
end
|
142
|
-
else
|
143
|
-
STDERR.puts "cookbooks directory not found, validation and downloading skipped"
|
144
|
-
end
|
145
|
-
create += "knife cookbook upload #{cb}\n"
|
146
|
-
#flatten list of cookbooks for validation later
|
147
|
-
cookbook_list.push(cb)
|
148
|
-
end
|
44
|
+
cookbook_list = Spiceweasel::CookbookList.new(input['cookbooks'])
|
45
|
+
environment_list = Spiceweasel::EnvironmentList.new(input['environments'])
|
46
|
+
role_list = Spiceweasel::RoleList.new(input['roles'])
|
47
|
+
data_bag_list = Spiceweasel::DataBagList.new(input['data bags'])
|
149
48
|
|
150
|
-
|
151
|
-
environments = input['environments'] || []
|
152
|
-
environment_list = []
|
153
|
-
environments.each do |environment|
|
154
|
-
STDOUT.puts "DEBUG: environment: #{environment.keys[0]}" if cli.config[:debug]
|
155
|
-
delete += "knife environment delete #{environment.keys[0]} -y\n"
|
156
|
-
create += "knife environment from file #{environment.keys[0]}.rb\n"
|
157
|
-
#flatten list of environments for validation later
|
158
|
-
environment_list.push(environment.keys[0])
|
159
|
-
end
|
49
|
+
node_list = Spiceweasel::NodeList.new(input['nodes'], cookbook_list, environment_list, role_list)
|
160
50
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
delete += "knife role delete #{role.keys[0]} -y\n"
|
167
|
-
create += "knife role from file #{role.keys[0]}.rb\n"
|
168
|
-
#flatten list of roles for validation later
|
169
|
-
role_list.push(role.keys[0])
|
170
|
-
end
|
51
|
+
create += cookbook_list.create
|
52
|
+
create += environment_list.create
|
53
|
+
create += role_list.create
|
54
|
+
create += data_bag_list.create
|
55
|
+
create += node_list.create
|
171
56
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
create += "knife data bag create #{bag.keys[0]}\n"
|
178
|
-
items = bag[bag.keys[0]] || []
|
179
|
-
secret = nil
|
180
|
-
while item = items.shift
|
181
|
-
STDOUT.puts "DEBUG: data bag #{bag.keys[0]} item: #{item}" if cli.config[:debug]
|
182
|
-
if item.start_with?("secret")
|
183
|
-
secret = item.split()[1]
|
184
|
-
next
|
185
|
-
end
|
186
|
-
if item =~ /\*/ #wildcard support
|
187
|
-
files = Dir.glob("data_bags/#{bag.keys[0]}/#{item}.json")
|
188
|
-
items += files.collect {|x| x[x.rindex('/')+1..-6]}
|
189
|
-
puts items
|
190
|
-
next
|
191
|
-
end
|
192
|
-
if secret
|
193
|
-
create += "knife data bag from file #{bag.keys[0]} data_bags/#{bag.keys[0]}/#{item}.json --secret-file #{secret}\n"
|
194
|
-
else
|
195
|
-
create += "knife data bag from file #{bag.keys[0]} data_bags/#{bag.keys[0]}/#{item}.json\n"
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
nodes = input['nodes'] || []
|
201
|
-
#currently use bulk_delete for deleting, add provider support real soon
|
202
|
-
delete += "knife node bulk_delete .* -y\n"
|
203
|
-
nodes.each do |node|
|
204
|
-
STDOUT.puts "DEBUG: node: #{node.keys[0]}" if cli.config[:debug]
|
205
|
-
# delete += "knife node delete #{node[node.keys[0]}\n"
|
206
|
-
run_list = node[node.keys[0]][0].gsub(/ /,',').split(',')
|
207
|
-
STDOUT.puts "DEBUG: node run_list: #{run_list}" if cli.config[:debug]
|
208
|
-
validate_run_list(run_list, cookbook_list, environment_list, role_list)
|
209
|
-
#provider support
|
210
|
-
if node.keys[0].start_with?("bluebox","ec2","openstack","rackspace","slicehost","terremark")
|
211
|
-
provider = node.keys[0].split()
|
212
|
-
count = 1
|
213
|
-
if (provider.length == 2)
|
214
|
-
count = provider[1]
|
215
|
-
end
|
216
|
-
#create the instances
|
217
|
-
count.to_i.times do
|
218
|
-
create += "knife #{provider[0]} server create #{node[node.keys[0]][1]}"
|
219
|
-
if run_list.length > 0
|
220
|
-
create += " -r '#{node[node.keys[0]][0].gsub(/ /,',')}'\n"
|
221
|
-
end
|
222
|
-
end
|
223
|
-
else #multinode support
|
224
|
-
node.keys[0].split.each do |server|
|
225
|
-
create += "knife bootstrap #{server} #{node[node.keys[0]][1]}"
|
226
|
-
if run_list.length > 0
|
227
|
-
create += " -r '#{node[node.keys[0]][0].gsub(/ /,',')}'\n"
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
57
|
+
delete += cookbook_list.delete
|
58
|
+
delete += environment_list.delete
|
59
|
+
delete += role_list.delete
|
60
|
+
delete += data_bag_list.delete
|
61
|
+
delete += node_list.delete
|
232
62
|
|
233
63
|
#just print the knife commands, do not execute
|
234
64
|
#if cli.config[:dryrun]
|
235
|
-
if cli.config[:delete]
|
65
|
+
if cli.config[:delete]
|
236
66
|
puts delete unless delete.empty?
|
237
67
|
elsif cli.config[:rebuild]
|
238
68
|
puts delete unless delete.empty?
|
239
69
|
puts create unless create.empty?
|
240
70
|
else
|
241
71
|
puts create unless create.empty?
|
242
|
-
end
|
72
|
+
end
|
243
73
|
#else
|
244
74
|
#eventually we will execute instead of printing knife commands
|
245
75
|
#puts "BAM!"
|
data/lib/spiceweasel.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
1
|
module Spiceweasel
|
2
|
-
|
2
|
+
autoload :CLI, 'spiceweasel/cli'
|
3
|
+
autoload :RunList, 'spiceweasel/run_list'
|
4
|
+
autoload :CookbookList, 'spiceweasel/cookbook_list'
|
5
|
+
autoload :EnvironmentList, 'spiceweasel/environment_list'
|
6
|
+
autoload :RoleList, 'spiceweasel/role_list'
|
7
|
+
autoload :DataBagList, 'spiceweasel/data_bag_list'
|
8
|
+
autoload :NodeList, 'spiceweasel/node_list'
|
3
9
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'mixlib/cli'
|
2
|
+
require 'spiceweasel/version'
|
3
|
+
|
4
|
+
class Spiceweasel::CLI
|
5
|
+
include Mixlib::CLI
|
6
|
+
|
7
|
+
banner("Usage: spiceweasel [option] file")
|
8
|
+
|
9
|
+
option :debug,
|
10
|
+
:long => "--debug",
|
11
|
+
:description => "Verbose debugging messages.",
|
12
|
+
:boolean => true
|
13
|
+
|
14
|
+
option :delete,
|
15
|
+
:short => "-d",
|
16
|
+
:long => "--delete",
|
17
|
+
:description => "Print the knife commands to be delete the infrastructure",
|
18
|
+
:boolean => true
|
19
|
+
|
20
|
+
option :dryrun,
|
21
|
+
:long => "--dryrun",
|
22
|
+
:description => "Print the knife commands to be executed to STDOUT",
|
23
|
+
:boolean => true
|
24
|
+
|
25
|
+
option :help,
|
26
|
+
:short => "-h",
|
27
|
+
:long => "--help",
|
28
|
+
:description => "Show this message",
|
29
|
+
:on => :tail,
|
30
|
+
:boolean => true,
|
31
|
+
:show_options => true,
|
32
|
+
:exit => 0
|
33
|
+
|
34
|
+
option :rebuild,
|
35
|
+
:short => "-r",
|
36
|
+
:long => "--rebuild",
|
37
|
+
:description => "Print the knife commands to be delete and recreate the infrastructure",
|
38
|
+
:boolean => true
|
39
|
+
|
40
|
+
option :version,
|
41
|
+
:short => "-v",
|
42
|
+
:long => "--version",
|
43
|
+
:description => "Version",
|
44
|
+
:boolean => true,
|
45
|
+
:proc => lambda {|v| puts "Spiceweasel: #{Spiceweasel::VERSION}" },
|
46
|
+
:exit => 0
|
47
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Spiceweasel::CookbookList
|
2
|
+
def initialize(cookbooks = [])
|
3
|
+
@create = @delete = ''
|
4
|
+
@cookbooks = []
|
5
|
+
cookbooks.each do |cookbook|
|
6
|
+
cb = cookbook.keys.first
|
7
|
+
if cookbook[cb] and cookbook[cb].length > 0
|
8
|
+
version = cookbook[cb][0].to_s || ""
|
9
|
+
args = cookbook[cb][1] || ""
|
10
|
+
end
|
11
|
+
STDOUT.puts "DEBUG: cookbook: #{cb} #{version}" if DEBUG
|
12
|
+
@delete += "knife cookbook @delete #{cb} #{version} -y\n"
|
13
|
+
if File.directory?("cookbooks")
|
14
|
+
if version and File.directory?("cookbooks/#{cb}")
|
15
|
+
#check metadata.rb for requested version
|
16
|
+
metadata = File.open("cookbooks/#{cb}/metadata.rb").grep(/^version/)[0].split()[1].gsub(/"/,'').to_s
|
17
|
+
if (metadata != version)
|
18
|
+
raise "Invalid version #{version} of '#{cb}' requested, #{metadata} is already in the cookbooks directory."
|
19
|
+
exit(-1)
|
20
|
+
end
|
21
|
+
elsif !File.directory?("cookbooks/#{cb}")
|
22
|
+
@create += "knife cookbook site download #{cb} #{version} --file cookbooks/#{cb}.tgz #{args}\n"
|
23
|
+
@create += "tar -C cookbooks/ -xf cookbooks/#{cb}.tgz\n"
|
24
|
+
end
|
25
|
+
else
|
26
|
+
STDERR.puts "cookbooks directory not found, validation and downloading skipped"
|
27
|
+
end
|
28
|
+
@create += "knife cookbook upload #{cb}\n"
|
29
|
+
|
30
|
+
@cookbooks << cb
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :cookbooks, :create, :delete
|
35
|
+
|
36
|
+
def member?(cookbook)
|
37
|
+
cookbooks.include?(cookbook)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Spiceweasel::DataBagList
|
2
|
+
def initialize(data_bags = [])
|
3
|
+
@create = @delete = ''
|
4
|
+
data_bags.each do |data_bag|
|
5
|
+
STDOUT.puts "DEBUG: data bag: #{data_bag.keys[0]}" if DEBUG
|
6
|
+
@delete += "knife data bag delete #{data_bag.keys[0]} -y\n"
|
7
|
+
@create += "knife data bag create #{data_bag.keys[0]}\n"
|
8
|
+
items = data_bag[data_bag.keys[0]] || []
|
9
|
+
secret = nil
|
10
|
+
while item = items.shift
|
11
|
+
STDOUT.puts "DEBUG: data bag #{data_bag.keys[0]} item: #{item}" if DEBUG
|
12
|
+
if item.start_with?("secret")
|
13
|
+
secret = item.split()[1]
|
14
|
+
next
|
15
|
+
end
|
16
|
+
if item =~ /\*/ #wildcard support
|
17
|
+
files = Dir.glob("data_bags/#{data_bag.keys[0]}/#{item}.json")
|
18
|
+
items += files.collect {|x| x[x.rindex('/')+1..-6]}
|
19
|
+
puts items
|
20
|
+
next
|
21
|
+
end
|
22
|
+
if secret
|
23
|
+
@create += "knife data bag from file #{data_bag.keys[0]} data_bags/#{data_bag.keys[0]}/#{item}.json --secret-file #{secret}\n"
|
24
|
+
else
|
25
|
+
@create += "knife data bag from file #{data_bag.keys[0]} data_bags/#{data_bag.keys[0]}/#{item}.json\n"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :create, :delete
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Spiceweasel::EnvironmentList
|
2
|
+
def initialize(environments = [])
|
3
|
+
@create = @delete = ''
|
4
|
+
@environments = []
|
5
|
+
environments.each do |environment|
|
6
|
+
STDOUT.puts "DEBUG: environment: #{environment.keys[0]}" if DEBUG
|
7
|
+
@delete += "knife environment delete #{environment.keys[0]} -y\n"
|
8
|
+
@create += "knife environment from file #{environment.keys[0]}.rb\n"
|
9
|
+
@environments << environment.keys[0]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :environments, :create, :delete
|
14
|
+
|
15
|
+
def member?(environment)
|
16
|
+
environments.include?(environment)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Spiceweasel::NodeList
|
2
|
+
def initialize(nodes, cookbook_list, environment_list, role_list)
|
3
|
+
nodes ||= []
|
4
|
+
@create = @delete = ''
|
5
|
+
|
6
|
+
@delete += "knife node bulk_delete .* -y\n"
|
7
|
+
nodes.each do |node|
|
8
|
+
STDOUT.puts "DEBUG: node: #{node.keys[0]}" if DEBUG
|
9
|
+
run_list = node[node.keys[0]][0].gsub(/ /,',').split(',')
|
10
|
+
STDOUT.puts "DEBUG: node run_list: #{run_list}" if DEBUG
|
11
|
+
Spiceweasel::RunList.new(run_list).validate(cookbook_list, environment_list, role_list)
|
12
|
+
#provider support
|
13
|
+
if node.keys[0].start_with?("bluebox","ec2","openstack","rackspace","slicehost","terremark")
|
14
|
+
provider = node.keys[0].split()
|
15
|
+
count = 1
|
16
|
+
if (provider.length == 2)
|
17
|
+
count = provider[1]
|
18
|
+
end
|
19
|
+
#create the instances
|
20
|
+
count.to_i.times do
|
21
|
+
@create += "knife #{provider[0]} server create #{node[node.keys[0]][1]}"
|
22
|
+
if run_list.length > 0
|
23
|
+
@create += " -r '#{node[node.keys[0]][0].gsub(/ /,',')}'\n"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
else #multinode support
|
27
|
+
node.keys[0].split.each do |server|
|
28
|
+
@create += "knife bootstrap #{server} #{node[node.keys[0]][1]}"
|
29
|
+
if run_list.length > 0
|
30
|
+
@create += " -r '#{node[node.keys[0]][0].gsub(/ /,',')}'\n"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :create, :delete
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Spiceweasel::RoleList
|
2
|
+
def initialize(roles = [])
|
3
|
+
@create = @delete = ''
|
4
|
+
@roles = []
|
5
|
+
roles.each do |role|
|
6
|
+
STDOUT.puts "DEBUG: role: #{role.keys[0]}" if DEBUG
|
7
|
+
@delete += "knife role delete #{role.keys[0]} -y\n"
|
8
|
+
@create += "knife role from file #{role.keys[0]}.rb\n"
|
9
|
+
@roles << role.keys[0]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :roles, :create, :delete
|
14
|
+
|
15
|
+
def member?(role)
|
16
|
+
roles.include?(role)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Spiceweasel::RunList
|
2
|
+
def initialize(run_list)
|
3
|
+
@run_list = run_list
|
4
|
+
end
|
5
|
+
|
6
|
+
def validate(cookbooks, environments, roles)
|
7
|
+
@run_list.each do |item|
|
8
|
+
if item.start_with?("recipe")
|
9
|
+
#recipe[foo] or recipe[foo::bar]
|
10
|
+
recipe = item.slice(7..-2).split("::")[0]
|
11
|
+
unless cookbooks.member?(recipe)
|
12
|
+
raise "'#{item}' is an invalid run_list recipe not managed by spiceweasel"
|
13
|
+
exit(-1)
|
14
|
+
end
|
15
|
+
elsif item.start_with?("environment")
|
16
|
+
#environment[blah]
|
17
|
+
environment = item.slice(12..-2)
|
18
|
+
unless environments.member?(environment)
|
19
|
+
raise "'#{item}' is an invalid run_list environment not managed by spiceweasel"
|
20
|
+
exit(-1)
|
21
|
+
end
|
22
|
+
elsif item.start_with?("role")
|
23
|
+
#role[blah]
|
24
|
+
role = item.slice(5..-2)
|
25
|
+
unless roles.member?(role)
|
26
|
+
raise "'#{item}' is an invalid run_list role not managed by spiceweasel"
|
27
|
+
exit(-1)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
raise "'#{item}' is an invalid run_list component"
|
31
|
+
exit(-1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/spiceweasel/version.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Cover-all spec to prevent regressions during refactor
|
2
|
+
describe 'The Spiceweasel binary' do
|
3
|
+
before(:each) do
|
4
|
+
@expected_output = <<-OUTPUT
|
5
|
+
knife cookbook upload apache2
|
6
|
+
knife cookbook upload apt
|
7
|
+
knife cookbook upload mysql
|
8
|
+
knife environment from file development.rb
|
9
|
+
knife environment from file qa.rb
|
10
|
+
knife environment from file production.rb
|
11
|
+
knife role from file base.rb
|
12
|
+
knife role from file monitoring.rb
|
13
|
+
knife role from file webserver.rb
|
14
|
+
knife data bag create users
|
15
|
+
knife data bag from file users data_bags/users/alice.json
|
16
|
+
knife data bag from file users data_bags/users/bob.json
|
17
|
+
knife data bag from file users data_bags/users/chuck.json
|
18
|
+
knife data bag create data
|
19
|
+
knife data bag create passwords
|
20
|
+
knife data bag from file passwords data_bags/passwords/mysql.json --secret-file secret_key
|
21
|
+
knife data bag from file passwords data_bags/passwords/rabbitmq.json --secret-file secret_key
|
22
|
+
knife bootstrap serverA -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -r 'role[base]'
|
23
|
+
knife bootstrap serverB -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -r 'role[base]'
|
24
|
+
knife bootstrap serverC -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -r 'role[base]'
|
25
|
+
knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -r 'role[webserver],recipe[mysql::client]'
|
26
|
+
knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -r 'role[webserver],recipe[mysql::client]'
|
27
|
+
knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -r 'role[webserver],recipe[mysql::client]'
|
28
|
+
knife rackspace server create --image 49 --flavor 2 -r 'recipe[mysql],role[monitoring]'
|
29
|
+
knife rackspace server create --image 49 --flavor 2 -r 'recipe[mysql],role[monitoring]'
|
30
|
+
knife rackspace server create --image 49 --flavor 2 -r 'recipe[mysql],role[monitoring]'
|
31
|
+
OUTPUT
|
32
|
+
|
33
|
+
@spiceweasel_binary = File.join(File.dirname(__FILE__), *%w[.. .. bin spiceweasel])
|
34
|
+
end
|
35
|
+
|
36
|
+
it "maintains consistent output from the example config" do
|
37
|
+
`#{@spiceweasel_binary} --dryrun example.yml`.should == @expected_output
|
38
|
+
end
|
39
|
+
end
|
metadata
CHANGED
@@ -1,27 +1,62 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spiceweasel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease: false
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 7
|
9
|
-
- 1
|
10
|
-
version: 0.7.1
|
4
|
+
version: "0.8"
|
11
5
|
platform: ruby
|
12
6
|
authors:
|
13
7
|
- Matt Ray
|
8
|
+
- Elliot Crosby-McCullough
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
12
|
|
18
|
-
date: 2011-
|
13
|
+
date: 2011-07-21 00:00:00 -05:00
|
19
14
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: chef
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: json
|
28
|
+
type: :runtime
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: mixlib-cli
|
38
|
+
type: :runtime
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
type: :development
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
22
56
|
description: This provides a CLI for generating knife commands to build Chef-managed infrastructure from a simple YAML file.
|
23
57
|
email:
|
24
58
|
- matt@opscode.com
|
59
|
+
- elliot.cm@gmail.com
|
25
60
|
executables:
|
26
61
|
- spiceweasel
|
27
62
|
extensions: []
|
@@ -29,20 +64,19 @@ extensions: []
|
|
29
64
|
extra_rdoc_files: []
|
30
65
|
|
31
66
|
files:
|
32
|
-
- .gitignore
|
33
|
-
- CHANGELOG.md
|
34
|
-
- Gemfile
|
35
|
-
- README.md
|
36
|
-
- Rakefile
|
37
67
|
- bin/spiceweasel
|
38
|
-
-
|
39
|
-
-
|
40
|
-
- lib/spiceweasel.rb
|
68
|
+
- lib/spiceweasel/cli.rb
|
69
|
+
- lib/spiceweasel/cookbook_list.rb
|
70
|
+
- lib/spiceweasel/data_bag_list.rb
|
71
|
+
- lib/spiceweasel/environment_list.rb
|
72
|
+
- lib/spiceweasel/node_list.rb
|
73
|
+
- lib/spiceweasel/role_list.rb
|
74
|
+
- lib/spiceweasel/run_list.rb
|
41
75
|
- lib/spiceweasel/version.rb
|
42
|
-
- spiceweasel.
|
43
|
-
-
|
76
|
+
- lib/spiceweasel.rb
|
77
|
+
- README.md
|
44
78
|
has_rdoc: true
|
45
|
-
homepage:
|
79
|
+
homepage: http://github.com/mattray/spiceweasel
|
46
80
|
licenses: []
|
47
81
|
|
48
82
|
post_install_message:
|
@@ -51,29 +85,23 @@ rdoc_options: []
|
|
51
85
|
require_paths:
|
52
86
|
- lib
|
53
87
|
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
-
none: false
|
55
88
|
requirements:
|
56
89
|
- - ">="
|
57
90
|
- !ruby/object:Gem::Version
|
58
|
-
hash: 3
|
59
|
-
segments:
|
60
|
-
- 0
|
61
91
|
version: "0"
|
92
|
+
version:
|
62
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
-
none: false
|
64
94
|
requirements:
|
65
95
|
- - ">="
|
66
96
|
- !ruby/object:Gem::Version
|
67
|
-
hash: 3
|
68
|
-
segments:
|
69
|
-
- 0
|
70
97
|
version: "0"
|
98
|
+
version:
|
71
99
|
requirements: []
|
72
100
|
|
73
101
|
rubyforge_project: spiceweasel
|
74
|
-
rubygems_version: 1.3.
|
102
|
+
rubygems_version: 1.3.5
|
75
103
|
signing_key:
|
76
104
|
specification_version: 3
|
77
105
|
summary: CLI for generating Chef knife commands from a simple YAML file.
|
78
|
-
test_files:
|
79
|
-
|
106
|
+
test_files:
|
107
|
+
- spec/bin/spiceweasel_spec.rb
|
data/.gitignore
DELETED
data/CHANGELOG.md
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
This is the current, previous and future development milestones and contains the features backlog.
|
2
|
-
|
3
|
-
0.1
|
4
|
-
===
|
5
|
-
* initial README.md describing goals
|
6
|
-
* command-line tool
|
7
|
-
* basic options all supported
|
8
|
-
* create repo on GitHub
|
9
|
-
* publish as a gem on RubyGems
|
10
|
-
|
11
|
-
0.2
|
12
|
-
===
|
13
|
-
* switch to mixlib-cli
|
14
|
-
* --dryrun The option `--dryrun` will print the commands to run, but not actually execute them (the default behavior)
|
15
|
-
* --delete The option `--delete` will delete each cookbook, role, data bag, environment and node described in the yml file. All nodes from the system are deleted with `knife node bulk_delete`.
|
16
|
-
* --rebuild The option `--rebuild` will remove all currently managed infrastructure for this chef repository and rebuild it from scratch.
|
17
|
-
|
18
|
-
0.3
|
19
|
-
===
|
20
|
-
* renamed MILESTONES.md to CHANGELOG.md
|
21
|
-
* fixed version number
|
22
|
-
* updated YAML schema and examples because Ruby 1.8 does not order hashes.
|
23
|
-
* validate that the recipes and roles listed in the nodes are loaded
|
24
|
-
|
25
|
-
0.4
|
26
|
-
===
|
27
|
-
* support versions for cookbooks
|
28
|
-
* support site vendor for cookbooks
|
29
|
-
|
30
|
-
0.5
|
31
|
-
===
|
32
|
-
* support JSON and YAML
|
33
|
-
|
34
|
-
0.6
|
35
|
-
===
|
36
|
-
* add support for cookbook options
|
37
|
-
|
38
|
-
0.7
|
39
|
-
=============
|
40
|
-
* add support for environments
|
41
|
-
* rescue from parser errors
|
42
|
-
* update cookbook download syntax
|
43
|
-
* multiple nodes with same runlists syntax
|
44
|
-
* add support for encrypted data bags
|
45
|
-
* wildcard support for data bag items
|
46
|
-
|
47
|
-
0.7.1 *CURRENT*
|
48
|
-
===============
|
49
|
-
* fixed run list parsing
|
50
|
-
* updated examples for Chef 0.10
|
51
|
-
* relaxed validation on existing cookbooks
|
52
|
-
|
53
|
-
BACKLOG
|
54
|
-
=======
|
55
|
-
Next
|
56
|
-
----
|
57
|
-
* make spiceweasel a library rather than an executable
|
58
|
-
* switch to using lib/ directory
|
59
|
-
* convert to a knife plugin
|
60
|
-
* knife batch create from file infrastructure.yml
|
61
|
-
* knife batch delete from file infrastructure.json
|
62
|
-
* knife batch rebuild from file infrastructure.yml
|
63
|
-
Future
|
64
|
-
------
|
65
|
-
* --chef-client The option `--chef-client` will make a `knife ssh` call to each box and run `chef-client` on each.
|
66
|
-
* --chef-client validation that nodes are added
|
67
|
-
* -e/--execute execute the commands
|
68
|
-
* catching return codes and retrying (with retry count?)
|
69
|
-
* make the JSON calls directly with Chef APIs
|
70
|
-
* execution-phase validation
|
71
|
-
* check metadata.rb of cookbooks for their dependencies
|
72
|
-
* validate within role files rather than the names of files (assumption that they are the same)
|
73
|
-
* validate cookbooks referenced in roles
|
74
|
-
* validate not trying to use environments with 0.9.x
|
75
|
-
* validate within environment files rather than the names of files (assumption that they are the same)
|
76
|
-
* validate cookbooks referenced in environments
|
77
|
-
* validate recipes from cookbooks in run_lists
|
78
|
-
* wildcards for environments and roles
|
79
|
-
* on provider delete take count of vendor-specific, delete if match (ec2 server delete and node delete)
|
80
|
-
* knife winrm bootstrap FQDN [RUN LIST...] (options)
|
81
|
-
* use GNU parallel with knife?
|
82
|
-
* extract existing infrastructure
|
83
|
-
* knife batch extract
|
data/Gemfile
DELETED
data/Rakefile
DELETED
data/example.json
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"cookbooks":
|
3
|
-
[
|
4
|
-
{"apache2":[]},
|
5
|
-
{"apt":
|
6
|
-
[
|
7
|
-
"1.1.1"
|
8
|
-
]
|
9
|
-
},
|
10
|
-
{"mysql":[]}
|
11
|
-
],
|
12
|
-
"environments":
|
13
|
-
[
|
14
|
-
{"development":[]},
|
15
|
-
{"qa":[]},
|
16
|
-
{"production":[]}
|
17
|
-
],
|
18
|
-
"roles":
|
19
|
-
[
|
20
|
-
{"base":[]},
|
21
|
-
{"monitoring":[]},
|
22
|
-
{"webserver":[]}
|
23
|
-
],
|
24
|
-
"data bags":
|
25
|
-
[
|
26
|
-
{"users":
|
27
|
-
[
|
28
|
-
"alice",
|
29
|
-
"bob",
|
30
|
-
"chuck"
|
31
|
-
]
|
32
|
-
},
|
33
|
-
{"data":
|
34
|
-
[
|
35
|
-
"*"
|
36
|
-
]
|
37
|
-
},
|
38
|
-
{"passwords":
|
39
|
-
[
|
40
|
-
"secret secret_key",
|
41
|
-
"mysql",
|
42
|
-
"rabbitmq"
|
43
|
-
]
|
44
|
-
}
|
45
|
-
],
|
46
|
-
"nodes":
|
47
|
-
[
|
48
|
-
{"serverA":
|
49
|
-
[
|
50
|
-
"role[base]",
|
51
|
-
"-i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems"
|
52
|
-
]
|
53
|
-
},
|
54
|
-
{"serverB serverC":
|
55
|
-
[
|
56
|
-
"role[base]",
|
57
|
-
"-i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems"
|
58
|
-
]
|
59
|
-
},
|
60
|
-
{"ec2 3":
|
61
|
-
[
|
62
|
-
"role[webserver] recipe[mysql::client]",
|
63
|
-
"-S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small"
|
64
|
-
]
|
65
|
-
},
|
66
|
-
{"rackspace 3":
|
67
|
-
[
|
68
|
-
"recipe[mysql],role[monitoring]",
|
69
|
-
"--image 49 --flavor 2"
|
70
|
-
]
|
71
|
-
}
|
72
|
-
]
|
73
|
-
}
|
data/example.yml
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
cookbooks:
|
2
|
-
- apache2:
|
3
|
-
- apt:
|
4
|
-
- 1.1.1
|
5
|
-
- mysql:
|
6
|
-
|
7
|
-
environments:
|
8
|
-
- development:
|
9
|
-
- qa:
|
10
|
-
- production:
|
11
|
-
|
12
|
-
roles:
|
13
|
-
- base:
|
14
|
-
- monitoring:
|
15
|
-
- webserver:
|
16
|
-
|
17
|
-
data bags:
|
18
|
-
- users:
|
19
|
-
- alice
|
20
|
-
- bob
|
21
|
-
- chuck
|
22
|
-
- data:
|
23
|
-
- *
|
24
|
-
- passwords:
|
25
|
-
- secret secret_key
|
26
|
-
- mysql
|
27
|
-
- rabbitmq
|
28
|
-
|
29
|
-
nodes:
|
30
|
-
- serverA:
|
31
|
-
- role[base]
|
32
|
-
- -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems
|
33
|
-
- serverB serverC:
|
34
|
-
- role[base]
|
35
|
-
- -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems
|
36
|
-
- ec2 3:
|
37
|
-
- role[webserver] recipe[mysql::client]
|
38
|
-
- -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small
|
39
|
-
- rackspace 3:
|
40
|
-
- recipe[mysql],role[monitoring]
|
41
|
-
- --image 49 --flavor 2
|
data/spiceweasel.gemspec
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require "json"
|
4
|
-
require "mixlib/cli"
|
5
|
-
require "spiceweasel/version"
|
6
|
-
require "yaml"
|
7
|
-
|
8
|
-
|
9
|
-
Gem::Specification.new do |s|
|
10
|
-
s.name = "spiceweasel"
|
11
|
-
s.version = Spiceweasel::VERSION
|
12
|
-
s.platform = Gem::Platform::RUBY
|
13
|
-
s.authors = ["Matt Ray"]
|
14
|
-
s.email = ["matt@opscode.com"]
|
15
|
-
s.homepage = ""
|
16
|
-
s.summary = %q{CLI for generating Chef knife commands from a simple YAML file.}
|
17
|
-
s.description = %q{This provides a CLI for generating knife commands to build Chef-managed infrastructure from a simple YAML file.}
|
18
|
-
|
19
|
-
s.rubyforge_project = "spiceweasel"
|
20
|
-
|
21
|
-
s.files = `git ls-files`.split("\n")
|
22
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
23
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
-
s.require_paths = ["lib"]
|
25
|
-
end
|
data/test.sh
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
echo "------------>spiceweasel"
|
2
|
-
spiceweasel
|
3
|
-
echo "------------>Return code:$?"
|
4
|
-
echo "------------>spiceweasel -h"
|
5
|
-
spiceweasel -h
|
6
|
-
echo "------------>Return code:$?"
|
7
|
-
echo "------------>spiceweasel -h example.yml"
|
8
|
-
spiceweasel -h example.yml
|
9
|
-
echo "------------>Return code:$?"
|
10
|
-
echo "------------>spiceweasel -v"
|
11
|
-
spiceweasel -v
|
12
|
-
echo "------------>Return code:$?"
|
13
|
-
echo "------------>spiceweasel -v example.yml"
|
14
|
-
spiceweasel -v example.yml
|
15
|
-
echo "------------>Return code:$?"
|
16
|
-
echo "------------>spiceweasel --version"
|
17
|
-
spiceweasel --version
|
18
|
-
echo "------------>Return code:$?"
|
19
|
-
echo "------------>spiceweasel example.yml"
|
20
|
-
spiceweasel example.yml
|
21
|
-
echo "------------>Return code:$?"
|
22
|
-
echo "------------>spiceweasel --dryrun"
|
23
|
-
spiceweasel --dryrun
|
24
|
-
echo "------------>Return code:$?"
|
25
|
-
echo "------------>spiceweasel --dryrun example.yml"
|
26
|
-
spiceweasel --dryrun example.yml
|
27
|
-
echo "------------>Return code:$?"
|
28
|
-
echo "------------>spiceweasel --help"
|
29
|
-
spiceweasel --help
|
30
|
-
echo "------------>Return code:$?"
|
31
|
-
echo "------------>spiceweasel -d"
|
32
|
-
spiceweasel -d
|
33
|
-
echo "------------>Return code:$?"
|
34
|
-
echo "------------>spiceweasel -d example.yml"
|
35
|
-
spiceweasel -d example.yml
|
36
|
-
echo "------------>Return code:$?"
|
37
|
-
echo "------------>spiceweasel --delete example.yml"
|
38
|
-
spiceweasel --delete example.yml
|
39
|
-
echo "------------>Return code:$?"
|
40
|
-
echo "------------>spiceweasel -r"
|
41
|
-
spiceweasel -r
|
42
|
-
echo "------------>Return code:$?"
|
43
|
-
echo "------------>spiceweasel -r example.yml"
|
44
|
-
spiceweasel -r example.yml
|
45
|
-
echo "------------>Return code:$?"
|
46
|
-
echo "------------>spiceweasel --rebuild example.yml"
|
47
|
-
spiceweasel --rebuild example.yml
|
48
|
-
echo "------------>Return code:$?"
|