spiceweasel 0.9.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +40 -33
- data/bin/spiceweasel +24 -6
- data/lib/spiceweasel.rb +18 -1
- data/lib/spiceweasel/cli.rb +5 -0
- data/lib/spiceweasel/cookbook_list.rb +63 -14
- data/lib/spiceweasel/data_bag_list.rb +57 -10
- data/lib/spiceweasel/environment_list.rb +83 -9
- data/lib/spiceweasel/node_list.rb +58 -20
- data/lib/spiceweasel/role_list.rb +112 -9
- data/lib/spiceweasel/version.rb +19 -1
- data/spec/bin/spiceweasel_spec.rb +13 -8
- metadata +10 -23
- data/lib/spiceweasel/recipe_list.rb +0 -40
- data/lib/spiceweasel/run_list.rb +0 -36
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
Description
|
2
2
|
===========
|
3
|
-
Spiceweasel is a command-line tool for batch loading Chef infrastructure
|
3
|
+
Spiceweasel is a command-line tool for batch loading Chef infrastructure from a file.. It provides a simple syntax in either JSON or YAML for describing and deploying infrastructure in order with the Chef command-line tool `knife`. This manifest may be bundled with a Chef repository to deploy the infrastructure contained within the repository and validate that the components listed are all present.
|
4
4
|
|
5
|
-
The `examples` directory provides
|
5
|
+
The `examples` directory provides example manifests based on the Quick Starts provided at [http://help.opscode.com/kb/otherhelp](http://help.opscode.com/kb/otherhelp).
|
6
6
|
|
7
7
|
The [https://github.com/mattray/ravel-repo](https://github.com/mattray/ravel-repo) provides a working example for bootstrapping a Chef repository with Spiceweasel.
|
8
8
|
|
@@ -10,7 +10,7 @@ The [CHANGELOG.md](https://github.com/mattray/spiceweasel/blob/master/CHANGELOG.
|
|
10
10
|
|
11
11
|
Requirements
|
12
12
|
============
|
13
|
-
Spiceweasel currently depends on `knife` to run commands for it. Infrastructure files must either end in .json or .yml to be processed.
|
13
|
+
Spiceweasel currently depends on `knife` to run commands for it, but does not explicitly depend on the `chef` gem. Infrastructure files must either end in .json or .yml to be processed.
|
14
14
|
|
15
15
|
Written and tested initially with Chef 0.9.12 (should still work) and continuing development with the 0.10 series.
|
16
16
|
|
@@ -58,7 +58,7 @@ nodes:
|
|
58
58
|
- -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems
|
59
59
|
- serverB serverC:
|
60
60
|
- role[base]
|
61
|
-
- -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems
|
61
|
+
- -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -E production
|
62
62
|
- ec2 3:
|
63
63
|
- role[webserver] recipe[mysql::client]
|
64
64
|
- -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small
|
@@ -135,7 +135,7 @@ From the `example.json`:
|
|
135
135
|
{"serverB serverC":
|
136
136
|
[
|
137
137
|
"role[base]",
|
138
|
-
"-i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems"
|
138
|
+
"-i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -E production"
|
139
139
|
]
|
140
140
|
},
|
141
141
|
{"ec2 3":
|
@@ -168,7 +168,7 @@ From the `example.json`:
|
|
168
168
|
|
169
169
|
Cookbooks
|
170
170
|
---------
|
171
|
-
The `cookbooks` section of the
|
171
|
+
The `cookbooks` section of the manifest currently supports `knife cookbook upload FOO` where `FOO` is the name of the cookbook in the `cookbooks` directory. The default behavior is to download the cookbook as a tarball, untar it and remove the tarball. The `--siteinstall` option will allow for use of `knife cookbook site install` with the cookbook and the creation of a vendor branch if git is the underlying version control. If a version is passed, it is validated against the existing cookbook `metadata.rb` and it must match the `metadata.rb` string exactly. You may pass any additional arguments if necessary. Assuming the apt cookbook was not present, the example YAML snippet
|
172
172
|
|
173
173
|
``` yaml
|
174
174
|
cookbooks:
|
@@ -191,7 +191,7 @@ knife cookbook upload mysql
|
|
191
191
|
|
192
192
|
Environments
|
193
193
|
------------
|
194
|
-
The `environments` section of the
|
194
|
+
The `environments` section of the manifest currently supports `knife environment from file FOO` where `FOO` is the name of the environment file ending in `.rb` or `.json` in the `environments` directory. Validation is done to ensure the filename matches the environment and that any cookbooks referenced are listed in the manifest. The example YAML snippet
|
195
195
|
|
196
196
|
``` yaml
|
197
197
|
environments:
|
@@ -210,7 +210,7 @@ knife environment from file production.rb
|
|
210
210
|
|
211
211
|
Roles
|
212
212
|
-----
|
213
|
-
The `roles` section of the
|
213
|
+
The `roles` section of the manifest currently supports `knife role from file FOO` where `FOO` is the name of the role file ending in `.rb` or `.json` in the `roles` directory. Validation is done to ensure the filename matches the role name and that any cookbooks or roles referenced are listed in the manifest. The example YAML snippet
|
214
214
|
|
215
215
|
``` yaml
|
216
216
|
roles:
|
@@ -229,7 +229,7 @@ knife role from file webserver.rb
|
|
229
229
|
|
230
230
|
Data Bags
|
231
231
|
---------
|
232
|
-
The `data bags` section of the
|
232
|
+
The `data bags` section of the manifest currently creates the data bags listed with `knife data bag create FOO` where `FOO` is the name of the data bag. Individual items may be added to the data bag as part of a JSON or YAML sequence, the assumption is made that they `.json` files and in the proper `data_bags/FOO` directory. You may also pass a wildcard as an entry to load all matching data bags (ie. `*`). Encrypted data bags are supported by listing `secret filename` as the first item (where `filename` is the secret key to be used). Validation is done to ensure the JSON is properly formatted, the id matches and any secret keys are in the correct locations. Assuming the presence of `dataA.json` and `dataB.json` in the `data_bags/data` directory, the YAML snippet
|
233
233
|
|
234
234
|
``` yaml
|
235
235
|
data bags:
|
@@ -249,20 +249,22 @@ produces the knife commands
|
|
249
249
|
|
250
250
|
```
|
251
251
|
knife data bag create users
|
252
|
-
knife data bag from file users
|
253
|
-
knife data bag from file users
|
254
|
-
knife data bag from file users
|
252
|
+
knife data bag from file users alice.json
|
253
|
+
knife data bag from file users bob.json
|
254
|
+
knife data bag from file users chuck.json
|
255
255
|
knife data bag create data
|
256
|
-
knife data bag from file users
|
257
|
-
knife data bag from file users
|
256
|
+
knife data bag from file users dataA.json
|
257
|
+
knife data bag from file users dataB.json
|
258
258
|
knife data bag create passwords
|
259
|
-
knife data bag from file passwords
|
260
|
-
knife data bag from file passwords
|
259
|
+
knife data bag from file passwords mysql.json --secret-file secret_key
|
260
|
+
knife data bag from file passwords rabbitmq.json --secret-file secret_key
|
261
261
|
```
|
262
262
|
|
263
263
|
Nodes
|
264
264
|
-----
|
265
|
-
The `nodes` section of the
|
265
|
+
The `nodes` section of the manifest bootstraps a node for each entry where the entry is a hostname or provider and count. A shortcut syntax for bulk-creating nodes with various providers where the line starts with the provider and ends with the number of nodes to be provisioned. Windows nodes need to specify either `windows_winrm` or `windows_ssh` depending on the protocol used, followed by the name of the node(s). Each node requires 2 items after it in a sequence. You may also use the `--parallel` flag from the command line, allowing provider commands to run simultaneously for faster deployment.
|
266
|
+
|
267
|
+
The first item after the node is the run_list and the second are the CLI options used. The run_list may be space or comma-delimited. Validation is performed on the run_list components to ensure that only cookbooks and roles listed in the manifest are used. Validation on the options ensures that any Environments referenced are also listed. You may specify multiple nodes to have the same configuration by listing them separated by a space. The example YAML snippet
|
266
268
|
|
267
269
|
``` yaml
|
268
270
|
nodes:
|
@@ -271,7 +273,7 @@ nodes:
|
|
271
273
|
- -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems
|
272
274
|
- serverB serverC:
|
273
275
|
- role[base]
|
274
|
-
- -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems
|
276
|
+
- -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -E production
|
275
277
|
- ec2 3:
|
276
278
|
- role[webserver] recipe[mysql::client]
|
277
279
|
- -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small
|
@@ -289,15 +291,16 @@ nodes:
|
|
289
291
|
produces the knife commands
|
290
292
|
|
291
293
|
```
|
292
|
-
knife bootstrap serverA
|
293
|
-
knife bootstrap serverB
|
294
|
-
knife bootstrap serverC
|
295
|
-
knife ec2 server create
|
296
|
-
knife ec2 server create
|
297
|
-
knife ec2 server create
|
298
|
-
knife
|
299
|
-
knife rackspace server create 'recipe[mysql],role[monitoring]'
|
300
|
-
knife rackspace server create 'recipe[mysql],role[monitoring]'
|
294
|
+
knife bootstrap serverA -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -r 'role[base]'
|
295
|
+
knife bootstrap serverB -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -E production -r 'role[base]'
|
296
|
+
knife bootstrap serverC -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -E production -r 'role[base]'
|
297
|
+
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]'
|
298
|
+
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]'
|
299
|
+
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]'
|
300
|
+
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]'
|
301
|
+
knife rackspace server create --image 49 --flavor 2 -r 'recipe[mysql],role[monitoring]'
|
302
|
+
knife rackspace server create --image 49 --flavor 2 -r 'recipe[mysql],role[monitoring]'
|
303
|
+
knife rackspace server create --image 49 --flavor 2 -r 'recipe[mysql],role[monitoring]'
|
301
304
|
knife bootstrap windows winrm winboxA -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]'
|
302
305
|
knife bootstrap windows ssh winboxB -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]'
|
303
306
|
knife bootstrap windows ssh winboxC -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]'
|
@@ -305,7 +308,7 @@ knife bootstrap windows ssh winboxC -x Administrator -P 'super_secret_password'
|
|
305
308
|
|
306
309
|
Usage
|
307
310
|
=====
|
308
|
-
To
|
311
|
+
To parse a spiceweasel manifest, run the following from your Chef repository directory:
|
309
312
|
|
310
313
|
```
|
311
314
|
spiceweasel path/to/infrastructure.json
|
@@ -317,7 +320,7 @@ or
|
|
317
320
|
spiceweasel path/to/infrastructure.yml
|
318
321
|
```
|
319
322
|
|
320
|
-
This will generate the knife commands to build the described infrastructure. Infrastructure files must end in either `.json` or `.yml`.
|
323
|
+
This will generate the knife commands to build the described infrastructure. Infrastructure manifest files must end in either `.json` or `.yml`.
|
321
324
|
|
322
325
|
-c/--knifeconfig
|
323
326
|
----------------
|
@@ -329,7 +332,7 @@ This provides verbose debugging messages.
|
|
329
332
|
|
330
333
|
-d/--delete
|
331
334
|
-----------
|
332
|
-
The delete command will generate the knife commands to delete the infrastructure described in the
|
335
|
+
The delete command will generate the knife commands to delete the infrastructure described in the manifest. This includes each cookbook, environment, role, data bag and node listed. Node deletion will specify individual nodes, attempt to pass the list of nodes to the cloud provider for deletion, and finish with `knife node bulk delete`. If you are mixing individual nodes with cloud provider nodes it is possible that nodes may be missed from cloud provider deletion and you should double-check (ie. `knife ec2 server list`).
|
333
336
|
|
334
337
|
--dryrun
|
335
338
|
--------
|
@@ -339,13 +342,17 @@ This is the default action, printing the knife commands to be run without execut
|
|
339
342
|
---------
|
340
343
|
Print the currently-supported usage options for spiceweasel.
|
341
344
|
|
345
|
+
--novalidation
|
346
|
+
--------------
|
347
|
+
Disable validation ensuring existence of the cookbooks, environments, roles, data bags and nodes in infrastructure file.
|
348
|
+
|
342
349
|
--parallel
|
343
350
|
----------
|
344
|
-
Use the GNU 'parallel' command to execute 'knife VENDOR server create' commands that may be
|
351
|
+
Use the GNU 'parallel' command to execute 'knife VENDOR server create' commands that may be run simultaneously.
|
345
352
|
|
346
353
|
-r/--rebuild
|
347
|
-
|
348
|
-
The rebuild command will generate the knife commands to delete and recreate the infrastructure described in the
|
354
|
+
------------
|
355
|
+
The rebuild command will generate the knife commands to delete and recreate the infrastructure described in the manifest. This includes each cookbook, environment, role, data bag and node listed.
|
349
356
|
|
350
357
|
--siteinstall
|
351
358
|
-------------
|
data/bin/spiceweasel
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
#
|
4
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
5
|
+
#
|
6
|
+
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
3
21
|
require 'json'
|
4
22
|
require 'yaml'
|
5
23
|
|
@@ -15,6 +33,7 @@ begin
|
|
15
33
|
DEBUG = cli.config[:debug]
|
16
34
|
PARALLEL = cli.config[:parallel]
|
17
35
|
SITEINSTALL = cli.config[:siteinstall]
|
36
|
+
NOVALIDATION = cli.config[:novalidation]
|
18
37
|
rescue OptionParser::InvalidOption => e
|
19
38
|
STDERR.puts e.message
|
20
39
|
puts cli.opt_parser.to_s
|
@@ -33,15 +52,15 @@ begin
|
|
33
52
|
elsif (file.end_with?(".json"))
|
34
53
|
input = JSON.parse(File.read(ARGV.last))
|
35
54
|
else
|
36
|
-
|
55
|
+
STDERR.puts "ERROR: Unknown file type, please use a file ending with either '.json' or '.yml'."
|
37
56
|
exit(-1)
|
38
57
|
end
|
39
58
|
rescue JSON::ParserError => e
|
40
59
|
STDERR.puts e.message
|
41
|
-
STDERR.puts "Parsing error in the infrastructure file provided."
|
60
|
+
STDERR.puts "ERROR: Parsing error in the infrastructure file provided."
|
42
61
|
exit(-1)
|
43
62
|
rescue Exception
|
44
|
-
STDERR.puts "No infrastructure .json or .yml file provided."
|
63
|
+
STDERR.puts "ERROR: No infrastructure .json or .yml file provided."
|
45
64
|
puts cli.opt_parser.to_s
|
46
65
|
exit(-1)
|
47
66
|
end
|
@@ -50,10 +69,9 @@ create = String.new()
|
|
50
69
|
delete = String.new()
|
51
70
|
|
52
71
|
cookbook_list = Spiceweasel::CookbookList.new(input['cookbooks'], options)
|
53
|
-
environment_list = Spiceweasel::EnvironmentList.new(input['environments'], options)
|
54
|
-
role_list = Spiceweasel::RoleList.new(input['roles'], options)
|
72
|
+
environment_list = Spiceweasel::EnvironmentList.new(input['environments'], cookbook_list, options)
|
73
|
+
role_list = Spiceweasel::RoleList.new(input['roles'], environment_list, cookbook_list, options)
|
55
74
|
data_bag_list = Spiceweasel::DataBagList.new(input['data bags'], options)
|
56
|
-
|
57
75
|
node_list = Spiceweasel::NodeList.new(input['nodes'], cookbook_list, environment_list, role_list, options)
|
58
76
|
|
59
77
|
create += cookbook_list.create
|
data/lib/spiceweasel.rb
CHANGED
@@ -1,6 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
1
19
|
module Spiceweasel
|
2
20
|
autoload :CLI, 'spiceweasel/cli'
|
3
|
-
autoload :RunList, 'spiceweasel/run_list'
|
4
21
|
autoload :CookbookList, 'spiceweasel/cookbook_list'
|
5
22
|
autoload :EnvironmentList, 'spiceweasel/environment_list'
|
6
23
|
autoload :RoleList, 'spiceweasel/role_list'
|
data/lib/spiceweasel/cli.rb
CHANGED
@@ -36,6 +36,11 @@ class Spiceweasel::CLI
|
|
36
36
|
:long => "--knifeconfig CONFIG",
|
37
37
|
:description => "Specify the knife.rb configuration file to use"
|
38
38
|
|
39
|
+
option :novalidation,
|
40
|
+
:long => "--novalidation",
|
41
|
+
:description => "Disable validation",
|
42
|
+
:boolean => true
|
43
|
+
|
39
44
|
option :parallel,
|
40
45
|
:long => "--parallel",
|
41
46
|
:description => "Use the GNU 'parallel' command to parallelize 'knife VENDOR server create' commands that are not order-dependent",
|
@@ -1,7 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
1
19
|
class Spiceweasel::CookbookList
|
2
20
|
def initialize(cookbooks = [], options = {})
|
3
21
|
@create = @delete = ''
|
4
|
-
@
|
22
|
+
@cookbook_list = {}
|
23
|
+
@dependencies = []
|
24
|
+
#validate each of the cookbooks specified in the manifest
|
5
25
|
if cookbooks
|
6
26
|
cookbooks.each do |cookbook|
|
7
27
|
cb = cookbook.keys.first
|
@@ -10,16 +30,10 @@ class Spiceweasel::CookbookList
|
|
10
30
|
args = cookbook[cb][1] || ""
|
11
31
|
end
|
12
32
|
STDOUT.puts "DEBUG: cookbook: #{cb} #{version}" if DEBUG
|
13
|
-
@delete += "knife cookbook#{options['knife_options']} delete #{cb} #{version} -y\n"
|
14
33
|
if File.directory?("cookbooks")
|
15
|
-
if
|
16
|
-
|
17
|
-
|
18
|
-
if (metadata != version)
|
19
|
-
raise "Invalid version #{version} of '#{cb}' requested, #{metadata} is already in the cookbooks directory."
|
20
|
-
exit(-1)
|
21
|
-
end
|
22
|
-
elsif !File.directory?("cookbooks/#{cb}")
|
34
|
+
if File.directory?("cookbooks/#{cb}")
|
35
|
+
validateMetadata(cb,version) unless NOVALIDATION
|
36
|
+
else
|
23
37
|
if SITEINSTALL #use knife cookbook site install
|
24
38
|
@create += "knife cookbook#{options['knife_options']} site install #{cb} #{version} #{args}\n"
|
25
39
|
else #use knife cookbook site download, untar and then remove the tarball
|
@@ -29,18 +43,53 @@ class Spiceweasel::CookbookList
|
|
29
43
|
end
|
30
44
|
end
|
31
45
|
else
|
32
|
-
STDERR.puts "cookbooks directory not found,
|
46
|
+
STDERR.puts "'cookbooks' directory not found, unable to validate, download and load cookbooks" unless NOVALIDATION
|
33
47
|
end
|
34
48
|
@create += "knife cookbook#{options['knife_options']} upload #{cb}\n"
|
49
|
+
@delete += "knife cookbook#{options['knife_options']} delete #{cb} #{version} -y\n"
|
50
|
+
|
51
|
+
@cookbook_list[cb] = version
|
52
|
+
end
|
53
|
+
validateDependencies() unless NOVALIDATION
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#check the metadata for versions and gather deps
|
58
|
+
def validateMetadata(cookbook,version)
|
59
|
+
#check metadata.rb for requested version
|
60
|
+
metadata = File.open("cookbooks/#{cookbook}/metadata.rb").grep(/^version/)[0].split()[1].gsub(/"/,'').to_s
|
61
|
+
STDOUT.puts "DEBUG: cookbook metadata version: #{metadata}" if DEBUG
|
62
|
+
if version and (metadata != version)
|
63
|
+
STDERR.puts "ERROR: Invalid version '#{version}' of '#{cookbook}' requested, '#{metadata}' is already in the cookbooks directory."
|
64
|
+
exit(-1)
|
65
|
+
end
|
66
|
+
deps = File.open("cookbooks/#{cookbook}/metadata.rb").grep(/^depends/)
|
67
|
+
deps.each do |dependency|
|
68
|
+
line = dependency.split()
|
69
|
+
cbdep = ''
|
70
|
+
if line[1][0].eql?('"') #ignore variables and versions
|
71
|
+
cbdep = line[1].gsub(/"/,'')
|
72
|
+
cbdep.gsub!(/\,/,'') if cbdep.end_with?(',')
|
73
|
+
end
|
74
|
+
STDOUT.puts "DEBUG: #{cookbook} metadata depends: #{cbdep}" if DEBUG
|
75
|
+
@dependencies << cbdep
|
76
|
+
end
|
77
|
+
return @cookbook
|
78
|
+
end
|
35
79
|
|
36
|
-
|
80
|
+
#compare the list of cookbook deps with those specified
|
81
|
+
def validateDependencies()
|
82
|
+
@dependencies.each do |dep|
|
83
|
+
if !member?(dep)
|
84
|
+
STDERR.puts "ERROR: Cookbook dependency '#{dep}' is missing from the list of cookbooks in the manifest."
|
85
|
+
exit(-1)
|
37
86
|
end
|
38
87
|
end
|
39
88
|
end
|
40
89
|
|
41
|
-
attr_reader :
|
90
|
+
attr_reader :cookbook_list, :create, :delete
|
42
91
|
|
43
92
|
def member?(cookbook)
|
44
|
-
|
93
|
+
cookbook_list.keys.include?(cookbook)
|
45
94
|
end
|
46
95
|
end
|
@@ -1,34 +1,81 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'json'
|
20
|
+
|
1
21
|
class Spiceweasel::DataBagList
|
2
22
|
def initialize(data_bags = [], options = {})
|
3
23
|
@create = @delete = ''
|
4
24
|
if data_bags
|
25
|
+
if !File.directory?("data_bags")
|
26
|
+
STDERR.puts "ERROR: 'data_bags' directory not found, unable to validate or load data bag items" unless NOVALIDATION
|
27
|
+
end
|
5
28
|
data_bags.each do |data_bag|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
29
|
+
db = data_bag.keys[0]
|
30
|
+
STDOUT.puts "DEBUG: data bag: #{db}" if DEBUG
|
31
|
+
if !File.directory?("data_bags/#{db}")
|
32
|
+
STDERR.puts "ERROR: 'data_bags/#{db}' directory not found, unable to validate or load data bag items" unless NOVALIDATION
|
33
|
+
end
|
34
|
+
@create += "knife data bag#{options['knife_options']} create #{db}\n"
|
35
|
+
@delete += "knife data bag#{options['knife_options']} delete #{db} -y\n"
|
36
|
+
items = data_bag[db] || []
|
10
37
|
secret = nil
|
11
38
|
while item = items.shift
|
12
|
-
STDOUT.puts "DEBUG: data bag #{
|
39
|
+
STDOUT.puts "DEBUG: data bag #{db} item: #{item}" if DEBUG
|
13
40
|
if item.start_with?("secret")
|
14
41
|
secret = item.split()[1]
|
42
|
+
if !File.exists?(secret) and !NOVALIDATION
|
43
|
+
STDERR.puts "ERROR: secret key #{secret} not found, unable to load encrypted data bags for data bag #{db}."
|
44
|
+
exit(-1)
|
45
|
+
end
|
15
46
|
next
|
16
47
|
end
|
17
|
-
if item =~ /\*/ #wildcard support
|
18
|
-
files = Dir.glob("data_bags/#{
|
48
|
+
if item =~ /\*/ #wildcard support, will fail if directory not present
|
49
|
+
files = Dir.glob("data_bags/#{db}/#{item}.json")
|
19
50
|
items += files.collect {|x| x[x.rindex('/')+1..-6]}
|
20
|
-
puts items
|
51
|
+
STDOUT.puts "DEBUG: found items '#{items}' for data bag: #{db}" if DEBUG
|
21
52
|
next
|
22
53
|
end
|
54
|
+
validateItem(db, item) unless NOVALIDATION
|
23
55
|
if secret
|
24
|
-
@create += "knife data bag#{options['knife_options']} from file #{
|
56
|
+
@create += "knife data bag#{options['knife_options']} from file #{db} #{item}.json --secret-file #{secret}\n"
|
25
57
|
else
|
26
|
-
@create += "knife data bag#{options['knife_options']} from file #{
|
58
|
+
@create += "knife data bag#{options['knife_options']} from file #{db} #{item}.json\n"
|
27
59
|
end
|
28
60
|
end
|
29
61
|
end
|
30
62
|
end
|
31
63
|
end
|
32
64
|
|
65
|
+
#validate the item to be loaded
|
66
|
+
def validateItem(db, item)
|
67
|
+
if !File.exists?("data_bags/#{db}/#{item}.json")
|
68
|
+
STDERR.puts "ERROR: data bag '#{db}' item '#{item}' file 'data_bags/#{db}/#{item}.json' does not exist"
|
69
|
+
exit(-1)
|
70
|
+
end
|
71
|
+
f = File.read("data_bags/#{db}/#{item}.json")
|
72
|
+
itemfile = JSON.parse(f) #invalid JSON will throw a trace
|
73
|
+
#validate the id matches the file name
|
74
|
+
if !item.eql?(itemfile['id'])
|
75
|
+
STDERR.puts "ERROR: data bag '#{db}' item '#{item}' listed in the manifest does not match the id '#{itemfile['id']}' within the 'data_bags/#{db}/#{item}.json' file."
|
76
|
+
exit(-1)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
33
80
|
attr_reader :create, :delete
|
34
81
|
end
|
@@ -1,20 +1,94 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'json'
|
20
|
+
|
1
21
|
class Spiceweasel::EnvironmentList
|
2
|
-
def initialize(environments = [], options = {})
|
22
|
+
def initialize(environments = [], cookbooks = {}, options = {})
|
3
23
|
@create = @delete = ''
|
4
|
-
@
|
24
|
+
@environment_list = []
|
5
25
|
if environments
|
6
|
-
environments.each do |
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
26
|
+
environments.each do |env|
|
27
|
+
environment = env.keys[0]
|
28
|
+
STDOUT.puts "DEBUG: environment: #{environment}" if DEBUG
|
29
|
+
if File.directory?("environments")
|
30
|
+
validate(environment, cookbooks) unless NOVALIDATION
|
31
|
+
else
|
32
|
+
STDERR.puts "'environments' directory not found, unable to validate or load environments" unless NOVALIDATION
|
33
|
+
end
|
34
|
+
if File.exists?("environments/#{environment}.json")
|
35
|
+
@create += "knife environment#{options['knife_options']} from file #{environment}.json\n"
|
36
|
+
else #assume no .json means they want .rb and catchall for misssing dir
|
37
|
+
@create += "knife environment#{options['knife_options']} from file #{environment}.rb\n"
|
38
|
+
end
|
39
|
+
@delete += "knife environment#{options['knife_options']} delete #{environment} -y\n"
|
40
|
+
@environment_list << environment
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
#validate the content of the environment file
|
46
|
+
def validate(environment, cookbooks)
|
47
|
+
#validate the environments passed in match the name of either the .rb or .json
|
48
|
+
if File.exists?("environments/#{environment}.rb")
|
49
|
+
#validate that the name inside the file matches
|
50
|
+
name = File.open("environments/#{environment}.rb").grep(/^name/)[0].split()[1].gsub(/"/,'').to_s
|
51
|
+
if !environment.eql?(name)
|
52
|
+
STDERR.puts "ERROR: Environment '#{environment}' listed in the manifest does not match the name '#{name}' within the environments/#{environment}.rb file."
|
53
|
+
exit(-1)
|
54
|
+
end
|
55
|
+
#validate the cookbooks exist if they're mentioned
|
56
|
+
envcookbooks = File.open("environments/#{environment}.rb").grep(/^cookbook /)
|
57
|
+
envcookbooks.each do |cb|
|
58
|
+
dep = cb.split()[1].gsub(/"/,'').gsub(/,/,'')
|
59
|
+
STDOUT.puts "DEBUG: environment: '#{environment}' cookbook: '#{dep}'" if DEBUG
|
60
|
+
if !cookbooks.member?(dep)
|
61
|
+
STDERR.puts "ERROR: Cookbook dependency '#{dep}' from environment '#{environment}' is missing from the list of cookbooks in the manifest."
|
62
|
+
exit(-1)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
elsif File.exists?("environments/#{environment}.json")
|
66
|
+
#load the json, don't symbolize since we don't need json_class
|
67
|
+
f = File.read("environments/#{environment}.json")
|
68
|
+
envfile = JSON.parse(f, {symbolize_names: 'false'})
|
69
|
+
#validate that the name inside the file matches
|
70
|
+
STDOUT.puts "DEBUG: environment: '#{environment}' name: '#{envfile[:name]}'" if DEBUG
|
71
|
+
if !environment.eql?(envfile[:name])
|
72
|
+
STDERR.puts "ERROR: Environment '#{environment}' listed in the manifest does not match the name '#{envfile[:name]}' within the 'environments/#{environment}.json' file."
|
73
|
+
exit(-1)
|
74
|
+
end
|
75
|
+
#validate the cookbooks exist if they're mentioned
|
76
|
+
envfile[:cookbook_versions].keys.each do |cb|
|
77
|
+
STDOUT.puts "DEBUG: environment: '#{environment}' cookbook: '#{cb}'" if DEBUG
|
78
|
+
if !cookbooks.member?(cb.to_s)
|
79
|
+
STDERR.puts "ERROR: Cookbook dependency '#{cb}' from environment '#{environment}' is missing from the list of cookbooks in the manifest."
|
80
|
+
exit(-1)
|
81
|
+
end
|
11
82
|
end
|
83
|
+
else #environment is not here
|
84
|
+
STDERR.puts "ERROR: Invalid Environment '#{environment}' listed in the manifest but not found in the environments directory."
|
85
|
+
exit(-1)
|
12
86
|
end
|
13
87
|
end
|
14
88
|
|
15
|
-
attr_reader :
|
89
|
+
attr_reader :environment_list, :create, :delete
|
16
90
|
|
17
91
|
def member?(environment)
|
18
|
-
|
92
|
+
environment_list.include?(environment)
|
19
93
|
end
|
20
94
|
end
|
@@ -1,52 +1,55 @@
|
|
1
1
|
class Spiceweasel::NodeList
|
2
|
-
def initialize(nodes,
|
3
|
-
nodes ||= []
|
2
|
+
def initialize(nodes, cookbooks, environments, roles, options = {})
|
4
3
|
@create = @delete = ''
|
5
|
-
|
6
4
|
if nodes
|
7
5
|
nodes.each do |node|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
nname = node.keys[0]
|
7
|
+
STDOUT.puts "DEBUG: node: '#{nname}'" if DEBUG
|
8
|
+
#convert spaces to commas, drop multiple commas
|
9
|
+
run_list = node[nname][0].gsub(/ /,',').gsub(/,+/,',')
|
10
|
+
STDOUT.puts "DEBUG: node: 'node[nname]' run_list: '#{run_list}'" if DEBUG
|
11
|
+
validateRunList(nname, run_list, cookbooks, roles) unless NOVALIDATION
|
12
|
+
noptions = node[nname][1]
|
13
|
+
STDOUT.puts "DEBUG: node: 'node[nname]' options: '#{noptions}'" if DEBUG
|
14
|
+
validateOptions(nname, noptions, environments) unless NOVALIDATION
|
12
15
|
#provider support
|
13
|
-
if
|
14
|
-
provider =
|
16
|
+
if nname.start_with?("bluebox ","clodo ","cs ","ec2 ","gandi ","openstack ","rackspace ","slicehost ","terremark ","voxel ")
|
17
|
+
provider = nname.split()
|
15
18
|
count = 1
|
16
19
|
if (provider.length == 2)
|
17
20
|
count = provider[1]
|
18
21
|
end
|
19
22
|
if PARALLEL
|
20
23
|
@create += "seq #{count} | parallel -j 0 -v \""
|
21
|
-
@create += "knife #{provider[0]}#{options['knife_options']} server create #{
|
24
|
+
@create += "knife #{provider[0]}#{options['knife_options']} server create #{noptions}"
|
22
25
|
if run_list.length > 0
|
23
|
-
@create += " -r '#{
|
26
|
+
@create += " -r '#{run_list}'\"\n"
|
24
27
|
end
|
25
28
|
else
|
26
29
|
count.to_i.times do
|
27
|
-
@create += "knife #{provider[0]}#{options['knife_options']} server create #{
|
30
|
+
@create += "knife #{provider[0]}#{options['knife_options']} server create #{noptions}"
|
28
31
|
if run_list.length > 0
|
29
|
-
@create += " -r '#{
|
32
|
+
@create += " -r '#{run_list}'\n"
|
30
33
|
end
|
31
34
|
end
|
32
35
|
end
|
33
36
|
@delete += "knife node#{options['knife_options']} list | xargs knife #{provider[0]} server delete -y\n"
|
34
|
-
elsif
|
35
|
-
nodeline =
|
37
|
+
elsif nname.start_with?("windows") #windows node bootstrap support
|
38
|
+
nodeline = nname.split()
|
36
39
|
provider = nodeline.shift.split('_') #split on 'windows_ssh' etc
|
37
40
|
nodeline.each do |server|
|
38
|
-
@create += "knife bootstrap #{provider[0]} #{provider[1]}#{options['knife_options']} #{server} #{
|
41
|
+
@create += "knife bootstrap #{provider[0]} #{provider[1]}#{options['knife_options']} #{server} #{noptions}"
|
39
42
|
if run_list.length > 0
|
40
|
-
@create += " -r '#{
|
43
|
+
@create += " -r '#{run_list}'\n"
|
41
44
|
end
|
42
45
|
@delete += "knife node#{options['knife_options']} delete #{server} -y\n"
|
43
46
|
end
|
44
47
|
@delete += "knife node#{options['knife_options']} list | xargs knife #{provider[0]} server delete -y\n"
|
45
48
|
else #node bootstrap support
|
46
|
-
|
47
|
-
@create += "knife bootstrap#{options['knife_options']} #{server} #{
|
49
|
+
nname.split.each do |server|
|
50
|
+
@create += "knife bootstrap#{options['knife_options']} #{server} #{noptions}"
|
48
51
|
if run_list.length > 0
|
49
|
-
@create += " -r '#{
|
52
|
+
@create += " -r '#{run_list}'\n"
|
50
53
|
end
|
51
54
|
@delete += "knife node#{options['knife_options']} delete #{server} -y\n"
|
52
55
|
end
|
@@ -56,5 +59,40 @@ class Spiceweasel::NodeList
|
|
56
59
|
@delete += "knife node#{options['knife_options']} bulk delete .* -y\n"
|
57
60
|
end
|
58
61
|
|
62
|
+
#ensure run_list contents are listed previously.
|
63
|
+
def validateRunList(node, run_list, cookbooks, roles)
|
64
|
+
run_list.split(',').each do |item|
|
65
|
+
if item.start_with?("recipe[")
|
66
|
+
#recipe[foo] or recipe[foo::bar]
|
67
|
+
cb = item.split(/\[|\]/)[1].split(':')[0]
|
68
|
+
unless cookbooks.member?(cb)
|
69
|
+
STDERR.puts "ERROR: '#{node}' run list cookbook '#{cb}' is missing from the list of cookbooks in the manifest."
|
70
|
+
exit(-1)
|
71
|
+
end
|
72
|
+
elsif item.start_with?("role[")
|
73
|
+
#role[blah]
|
74
|
+
role = item.split(/\[|\]/)[1]
|
75
|
+
unless roles.member?(role)
|
76
|
+
STDERR.puts "ERROR: '#{node}' run list role '#{role}' is missing from the list of roles in the manifest."
|
77
|
+
exit(-1)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
STDERR.puts "ERROR: '#{node}' run list '#{item}' is an invalid run list entry in the manifest."
|
81
|
+
exit(-1)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#for now, just check that -E is legit
|
87
|
+
def validateOptions(node, options, environments)
|
88
|
+
if options =~ /-E/ #check for environments
|
89
|
+
env = options.split('-E')[1].split()[0]
|
90
|
+
unless environments.member?(env)
|
91
|
+
STDERR.puts "ERROR: '#{node}' environment '#{env}' is missing from the list of environments in the manifest."
|
92
|
+
exit(-1)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
59
97
|
attr_reader :create, :delete
|
60
98
|
end
|
@@ -1,20 +1,123 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'json'
|
20
|
+
|
1
21
|
class Spiceweasel::RoleList
|
2
|
-
def initialize(roles = [], options = {})
|
22
|
+
def initialize(roles = {}, environments = [], cookbooks = {}, options = {})
|
3
23
|
@create = @delete = ''
|
4
|
-
@
|
24
|
+
@role_list = []
|
5
25
|
if roles
|
6
|
-
roles.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
26
|
+
flatroles = roles.collect {|x| x.keys}.flatten
|
27
|
+
flatroles.each do |role|
|
28
|
+
STDOUT.puts "DEBUG: role: #{role}" if DEBUG
|
29
|
+
if File.directory?("roles")
|
30
|
+
validate(role, environments, cookbooks, flatroles) unless NOVALIDATION
|
31
|
+
else
|
32
|
+
STDERR.puts "ERROR: 'roles' directory not found, unable to validate or load roles" unless NOVALIDATION
|
33
|
+
end
|
34
|
+
if File.exists?("roles/#{role}.json")
|
35
|
+
@create += "knife role#{options['knife_options']} from file #{role}.json\n"
|
36
|
+
else #assume no .json means they want .rb and catchall for misssing dir
|
37
|
+
@create += "knife role#{options['knife_options']} from file #{role}.rb\n"
|
38
|
+
end
|
39
|
+
@delete += "knife role#{options['knife_options']} delete #{role} -y\n"
|
40
|
+
@role_list << role
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
#validate the content of the role file
|
46
|
+
def validate(role, environments, cookbooks, roles)
|
47
|
+
#validate the role passed in match the name of either the .rb or .json
|
48
|
+
if File.exists?("roles/#{role}.rb")
|
49
|
+
#validate that the name inside the file matches
|
50
|
+
name = File.open("roles/#{role}.rb").grep(/^name/)[0].split()[1].gsub(/"/,'').to_s
|
51
|
+
STDOUT.puts "DEBUG: role: '#{role}' name: '#{name}'" if DEBUG
|
52
|
+
if !role.eql?(name)
|
53
|
+
STDERR.puts "ERROR: Role '#{role}' listed in the manifest does not match the name '#{name}' within the roles/#{role}.rb file."
|
54
|
+
exit(-1)
|
55
|
+
end
|
56
|
+
#grab any lines with 'recipe[' or 'role['
|
57
|
+
rolerl = File.open("roles/#{role}.rb").grep(/recipe\[|role\[/)
|
58
|
+
rolerl.each do |line|
|
59
|
+
STDOUT.puts "DEBUG: role: '#{role}' line: '#{line}'" if DEBUG
|
60
|
+
line.strip.split(',').each do |rl|
|
61
|
+
if rl =~ /recipe\[/ #it's a cookbook
|
62
|
+
#split on the brackets and any colons
|
63
|
+
dep = rl.split(/\[|\]/)[1].split(':')[0]
|
64
|
+
STDOUT.puts "DEBUG: role: '#{role}' cookbook: '#{rl}': dep: '#{dep}'" if DEBUG
|
65
|
+
if !cookbooks.member?(dep)
|
66
|
+
STDERR.puts "ERROR: Cookbook dependency '#{dep}' from role '#{role}' is missing from the list of cookbooks in the manifest."
|
67
|
+
exit(-1)
|
68
|
+
end
|
69
|
+
elsif rl =~ /role\[/ #it's a role
|
70
|
+
#split on the brackets
|
71
|
+
dep = rl.split(/\[|\]/)[1]
|
72
|
+
STDOUT.puts "DEBUG: role: '#{role}' role: '#{rl}': dep: '#{dep}'" if DEBUG
|
73
|
+
if !roles.member?(dep)
|
74
|
+
STDERR.puts "ERROR: Role dependency '#{dep}' from role '#{role}' is missing from the list of roles in the manifest."
|
75
|
+
exit(-1)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
#TODO validate any environment-specific runlists
|
81
|
+
elsif File.exists?("roles/#{role}.json")
|
82
|
+
#load the json, don't symbolize since we don't need json_class
|
83
|
+
f = File.read("roles/#{role}.json")
|
84
|
+
rolefile = JSON.parse(f, {symbolize_names: 'false'})
|
85
|
+
#validate that the name inside the file matches
|
86
|
+
STDOUT.puts "DEBUG: role: '#{role}' name: '#{rolefile[:name]}'" if DEBUG
|
87
|
+
if !role.eql?(rolefile[:name])
|
88
|
+
STDERR.puts "ERROR: Role '#{role}' listed in the manifest does not match the name '#{rolefile[:name]}' within the 'roles/#{role}.json' file."
|
89
|
+
exit(-1)
|
90
|
+
end
|
91
|
+
#validate the cookbooks and roles exist if they're mentioned in run_lists
|
92
|
+
rolefile[:run_list].each do |rl|
|
93
|
+
if rl =~ /recipe\[/ #it's a cookbook
|
94
|
+
#split on the brackets and any colons
|
95
|
+
dep = rl.split(/\[|\]/)[1].split(':')[0]
|
96
|
+
STDOUT.puts "DEBUG: role: '#{role}' cookbook: '#{rl}': dep: '#{dep}'" if DEBUG
|
97
|
+
if !cookbooks.member?(dep)
|
98
|
+
STDERR.puts "ERROR: Cookbook dependency '#{dep}' from role '#{role}' is missing from the list of cookbooks in the manifest."
|
99
|
+
exit(-1)
|
100
|
+
end
|
101
|
+
elsif rl =~ /role\[/ #it's a role
|
102
|
+
#split on the brackets
|
103
|
+
dep = rl.split(/\[|\]/)[1]
|
104
|
+
STDOUT.puts "DEBUG: role: '#{role}' role: '#{rl}': dep: '#{dep}'" if DEBUG
|
105
|
+
if !roles.member?(dep)
|
106
|
+
STDERR.puts "ERROR: Role dependency '#{dep}' from role '#{role}' is missing from the list of roles in the manifest."
|
107
|
+
exit(-1)
|
108
|
+
end
|
109
|
+
end
|
11
110
|
end
|
111
|
+
#TODO validate any environment-specific runlists
|
112
|
+
else #role is not here
|
113
|
+
STDERR.puts "ERROR: Invalid Role '#{role}' listed in the manifest but not found in the roles directory."
|
114
|
+
exit(-1)
|
12
115
|
end
|
13
116
|
end
|
14
117
|
|
15
|
-
attr_reader :
|
118
|
+
attr_reader :role_list, :create, :delete
|
16
119
|
|
17
120
|
def member?(role)
|
18
|
-
|
121
|
+
role_list.include?(role)
|
19
122
|
end
|
20
123
|
end
|
data/lib/spiceweasel/version.rb
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
1
19
|
module Spiceweasel
|
2
|
-
VERSION = "0.
|
20
|
+
VERSION = "1.0.0"
|
3
21
|
end
|
@@ -9,31 +9,36 @@ knife environment from file development.rb
|
|
9
9
|
knife environment from file qa.rb
|
10
10
|
knife environment from file production.rb
|
11
11
|
knife role from file base.rb
|
12
|
+
knife role from file iisserver.rb
|
12
13
|
knife role from file monitoring.rb
|
13
14
|
knife role from file webserver.rb
|
14
15
|
knife data bag create users
|
15
|
-
knife data bag from file users
|
16
|
-
knife data bag from file users
|
17
|
-
knife data bag from file users
|
16
|
+
knife data bag from file users alice.json
|
17
|
+
knife data bag from file users bob.json
|
18
|
+
knife data bag from file users chuck.json
|
18
19
|
knife data bag create data
|
19
20
|
knife data bag create passwords
|
20
|
-
knife data bag from file passwords
|
21
|
-
knife data bag from file passwords
|
21
|
+
knife data bag from file passwords mysql.json --secret-file secret_key
|
22
|
+
knife data bag from file passwords rabbitmq.json --secret-file secret_key
|
22
23
|
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]'
|
24
|
+
knife bootstrap serverB -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -E production -r 'role[base]'
|
25
|
+
knife bootstrap serverC -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems -E production -r 'role[base]'
|
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]'
|
25
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]'
|
26
28
|
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
29
|
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
30
|
knife rackspace server create --image 49 --flavor 2 -r 'recipe[mysql],role[monitoring]'
|
29
31
|
knife rackspace server create --image 49 --flavor 2 -r 'recipe[mysql],role[monitoring]'
|
30
32
|
knife rackspace server create --image 49 --flavor 2 -r 'recipe[mysql],role[monitoring]'
|
33
|
+
knife bootstrap windows winrm winboxA -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]'
|
34
|
+
knife bootstrap windows ssh winboxB -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]'
|
35
|
+
knife bootstrap windows ssh winboxC -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]'
|
31
36
|
OUTPUT
|
32
37
|
|
33
38
|
@spiceweasel_binary = File.join(File.dirname(__FILE__), *%w[.. .. bin spiceweasel])
|
34
39
|
end
|
35
40
|
|
36
41
|
it "maintains consistent output from the example config" do
|
37
|
-
`#{@spiceweasel_binary} --dryrun example.yml`.should == @expected_output
|
42
|
+
`#{@spiceweasel_binary} --novalidation --dryrun example.yml`.should == @expected_output
|
38
43
|
end
|
39
44
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spiceweasel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,23 +10,12 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-12-
|
13
|
+
date: 2011-12-23 00:00:00.000000000 -06:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
|
-
- !ruby/object:Gem::Dependency
|
17
|
-
name: chef
|
18
|
-
requirement: &2164297640 !ruby/object:Gem::Requirement
|
19
|
-
none: false
|
20
|
-
requirements:
|
21
|
-
- - ! '>='
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: '0'
|
24
|
-
type: :runtime
|
25
|
-
prerelease: false
|
26
|
-
version_requirements: *2164297640
|
27
16
|
- !ruby/object:Gem::Dependency
|
28
17
|
name: json
|
29
|
-
requirement: &
|
18
|
+
requirement: &2151771000 !ruby/object:Gem::Requirement
|
30
19
|
none: false
|
31
20
|
requirements:
|
32
21
|
- - ! '>='
|
@@ -34,10 +23,10 @@ dependencies:
|
|
34
23
|
version: '0'
|
35
24
|
type: :runtime
|
36
25
|
prerelease: false
|
37
|
-
version_requirements: *
|
26
|
+
version_requirements: *2151771000
|
38
27
|
- !ruby/object:Gem::Dependency
|
39
28
|
name: mixlib-cli
|
40
|
-
requirement: &
|
29
|
+
requirement: &2151768280 !ruby/object:Gem::Requirement
|
41
30
|
none: false
|
42
31
|
requirements:
|
43
32
|
- - ! '>='
|
@@ -45,10 +34,10 @@ dependencies:
|
|
45
34
|
version: '0'
|
46
35
|
type: :runtime
|
47
36
|
prerelease: false
|
48
|
-
version_requirements: *
|
37
|
+
version_requirements: *2151768280
|
49
38
|
- !ruby/object:Gem::Dependency
|
50
39
|
name: rspec
|
51
|
-
requirement: &
|
40
|
+
requirement: &2151764400 !ruby/object:Gem::Requirement
|
52
41
|
none: false
|
53
42
|
requirements:
|
54
43
|
- - ! '>='
|
@@ -56,9 +45,9 @@ dependencies:
|
|
56
45
|
version: '0'
|
57
46
|
type: :development
|
58
47
|
prerelease: false
|
59
|
-
version_requirements: *
|
60
|
-
description:
|
61
|
-
infrastructure from a simple YAML file.
|
48
|
+
version_requirements: *2151764400
|
49
|
+
description: Provides a CLI tool for generating knife commands to build Chef-managed
|
50
|
+
infrastructure from a simple YAML or JSON file.
|
62
51
|
email:
|
63
52
|
- matt@opscode.com
|
64
53
|
- elliot.cm@gmail.com
|
@@ -73,9 +62,7 @@ files:
|
|
73
62
|
- lib/spiceweasel/data_bag_list.rb
|
74
63
|
- lib/spiceweasel/environment_list.rb
|
75
64
|
- lib/spiceweasel/node_list.rb
|
76
|
-
- lib/spiceweasel/recipe_list.rb
|
77
65
|
- lib/spiceweasel/role_list.rb
|
78
|
-
- lib/spiceweasel/run_list.rb
|
79
66
|
- lib/spiceweasel/version.rb
|
80
67
|
- lib/spiceweasel.rb
|
81
68
|
- README.md
|
@@ -1,40 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
-
#
|
4
|
-
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
#find a list of all the recipes provided by the included cookbooks for validation
|
20
|
-
class Spiceweasel::RecipeList
|
21
|
-
def initialize(cookbooks = [], options = {})
|
22
|
-
@recipes = []
|
23
|
-
if NOVALIDATION
|
24
|
-
STDOUT.puts "DEBUG: Skipping validation of recipes" if DEBUG
|
25
|
-
else
|
26
|
-
if cookbooks
|
27
|
-
cookbooks.each do |cookbook|
|
28
|
-
#check contents of each cookbook and pull out the recipes
|
29
|
-
#store in recipes in the form cookbook::recipe
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
attr_reader :recipes, :create, :delete
|
36
|
-
|
37
|
-
def member?(recipe)
|
38
|
-
recipes.include?(recipe)
|
39
|
-
end
|
40
|
-
end
|
data/lib/spiceweasel/run_list.rb
DELETED
@@ -1,36 +0,0 @@
|
|
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
|