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 CHANGED
@@ -1,8 +1,8 @@
1
1
  Description
2
2
  ===========
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`.
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 examples based on the Quick Starts provided at [http://help.opscode.com/kb/otherhelp](http://help.opscode.com/kb/otherhelp).
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 JSON or YAML file 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. The example YAML snippet
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 JSON or YAML file currently supports `knife environment from file FOO` where `FOO` is the name of the environment file ending in `.rb` in the `environments` directory. The example YAML snippet
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 JSON or YAML file currently supports `knife role from file FOO` where `FOO` is the name of the role file ending in `.rb` in the `roles` directory. The example YAML snippet
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 JSON or YAML file 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 `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). Assuming the presence of `dataA.json` and `dataB.json` in the `data_bags/data` directory, the YAML snippet
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 data_bags/users/alice.json
253
- knife data bag from file users data_bags/users/bob.json
254
- knife data bag from file users data_bags/users/chuck.json
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 data_bags/data/dataA.json
257
- knife data bag from file users data_bags/data/dataB.json
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 data_bags/passwords/mysql.json --secret-file secret_key
260
- knife data bag from file passwords data_bags/passwords/rabbitmq.json --secret-file secret_key
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 JSON or YAML file bootstraps a node for each entry where the entry is a hostname or provider and count. Each node requires 2 items after it in a sequence. The first item is the run_list and the second 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 recipes and roles listed in the file are used. You may specify multiple nodes to have the same configuration by listing them separated by a space. 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). The example YAML snippet
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 'role[base]' -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems
293
- knife bootstrap serverB 'role[base]' -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems
294
- knife bootstrap serverC 'role[base]' -i ~/.ssh/mray.pem -x user --sudo -d ubuntu10.04-gems
295
- knife ec2 server create 'role[webserver],recipe[mysql::client]' -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small
296
- knife ec2 server create 'role[webserver],recipe[mysql::client]' -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small
297
- knife ec2 server create 'role[webserver],recipe[mysql::client]' -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small
298
- knife rackspace server create 'recipe[mysql],role[monitoring]' --image 49 --flavor 2
299
- knife rackspace server create 'recipe[mysql],role[monitoring]' --image 49 --flavor 2
300
- knife rackspace server create 'recipe[mysql],role[monitoring]' --image 49 --flavor 2
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 run a spiceweasel file, run the following from you Chef repository directory:
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 file. This includes each cookbook, role, data bag, environment 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`).
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 parallelized.
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 file. This includes each cookbook, role, data bag, environment and node listed. Currently all nodes from the system are deleted with `knife node bulk_delete`, specific-node support will be added in a future release.
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
- raise "Unknown file type, please use a file ending with either '.json' or '.yml'."
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'
@@ -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
- @cookbooks = []
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 version and File.directory?("cookbooks/#{cb}")
16
- #check metadata.rb for requested version
17
- metadata = File.open("cookbooks/#{cb}/metadata.rb").grep(/^version/)[0].split()[1].gsub(/"/,'').to_s
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, validation and downloading skipped"
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
- @cookbooks << cb
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 :cookbooks, :create, :delete
90
+ attr_reader :cookbook_list, :create, :delete
42
91
 
43
92
  def member?(cookbook)
44
- cookbooks.include?(cookbook)
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
- STDOUT.puts "DEBUG: data bag: #{data_bag.keys[0]}" if DEBUG
7
- @delete += "knife data bag#{options['knife_options']} delete #{data_bag.keys[0]} -y\n"
8
- @create += "knife data bag#{options['knife_options']} create #{data_bag.keys[0]}\n"
9
- items = data_bag[data_bag.keys[0]] || []
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 #{data_bag.keys[0]} item: #{item}" if DEBUG
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/#{data_bag.keys[0]}/#{item}.json")
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 #{data_bag.keys[0]} data_bags/#{data_bag.keys[0]}/#{item}.json --secret-file #{secret}\n"
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 #{data_bag.keys[0]} data_bags/#{data_bag.keys[0]}/#{item}.json\n"
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
- @environments = []
24
+ @environment_list = []
5
25
  if environments
6
- environments.each do |environment|
7
- STDOUT.puts "DEBUG: environment: #{environment.keys[0]}" if DEBUG
8
- @delete += "knife environment#{options['knife_options']} delete #{environment.keys[0]} -y\n"
9
- @create += "knife environment#{options['knife_options']} from file #{environment.keys[0]}.rb\n"
10
- @environments << environment.keys[0]
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 :environments, :create, :delete
89
+ attr_reader :environment_list, :create, :delete
16
90
 
17
91
  def member?(environment)
18
- environments.include?(environment)
92
+ environment_list.include?(environment)
19
93
  end
20
94
  end
@@ -1,52 +1,55 @@
1
1
  class Spiceweasel::NodeList
2
- def initialize(nodes, cookbook_list, environment_list, role_list, options = {})
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
- 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)
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 node.keys[0].start_with?("bluebox","ec2","gandi","openstack","rackspace","slicehost","terremark","voxel")
14
- provider = node.keys[0].split()
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 #{node[node.keys[0]][1]}"
24
+ @create += "knife #{provider[0]}#{options['knife_options']} server create #{noptions}"
22
25
  if run_list.length > 0
23
- @create += " -r '#{node[node.keys[0]][0].gsub(/ /,',')}'\"\n"
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 #{node[node.keys[0]][1]}"
30
+ @create += "knife #{provider[0]}#{options['knife_options']} server create #{noptions}"
28
31
  if run_list.length > 0
29
- @create += " -r '#{node[node.keys[0]][0].gsub(/ /,',')}'\n"
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 node.keys[0].start_with?("windows") #windows node bootstrap support
35
- nodeline = node.keys[0].split()
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} #{node[node.keys[0]][1]}"
41
+ @create += "knife bootstrap #{provider[0]} #{provider[1]}#{options['knife_options']} #{server} #{noptions}"
39
42
  if run_list.length > 0
40
- @create += " -r '#{node[node.keys[0]][0].gsub(/ /,',')}'\n"
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
- node.keys[0].split.each do |server|
47
- @create += "knife bootstrap#{options['knife_options']} #{server} #{node[node.keys[0]][1]}"
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 '#{node[node.keys[0]][0].gsub(/ /,',')}'\n"
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
- @roles = []
24
+ @role_list = []
5
25
  if roles
6
- roles.each do |role|
7
- STDOUT.puts "DEBUG: role: #{role.keys[0]}" if DEBUG
8
- @delete += "knife role#{options['knife_options']} delete #{role.keys[0]} -y\n"
9
- @create += "knife role#{options['knife_options']} from file #{role.keys[0]}.rb\n"
10
- @roles << role.keys[0]
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 :roles, :create, :delete
118
+ attr_reader :role_list, :create, :delete
16
119
 
17
120
  def member?(role)
18
- roles.include?(role)
121
+ role_list.include?(role)
19
122
  end
20
123
  end
@@ -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.9.1"
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 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
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 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
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.9.1
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-14 00:00:00.000000000 -06:00
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: &2164297040 !ruby/object:Gem::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: *2164297040
26
+ version_requirements: *2151771000
38
27
  - !ruby/object:Gem::Dependency
39
28
  name: mixlib-cli
40
- requirement: &2164296500 !ruby/object:Gem::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: *2164296500
37
+ version_requirements: *2151768280
49
38
  - !ruby/object:Gem::Dependency
50
39
  name: rspec
51
- requirement: &2164295960 !ruby/object:Gem::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: *2164295960
60
- description: This provides a CLI for generating knife commands to build Chef-managed
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
@@ -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