spiceweasel 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # Description #
5
5
 
6
- Spiceweasel is a command-line tool for batch loading Chef infrastructure from a file. It provides a simple syntax in Ruby, 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.
6
+ Spiceweasel is a command-line tool for batch loading Chef infrastructure. It provides a simple syntax in Ruby, 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. The manifest may also be extracted from an existing repository.
7
7
 
8
8
  The `examples` directory provides example manifests based on the Quick Starts provided at http://help.opscode.com/kb/otherhelp. The https://github.com/mattray/vbacd-repo provides a working example for bootstrapping a Chef repository with Spiceweasel.
9
9
 
@@ -11,9 +11,9 @@ The [CHANGELOG.md](https://github.com/mattray/spiceweasel/blob/master/CHANGELOG.
11
11
 
12
12
  # Requirements #
13
13
 
14
- Spiceweasel currently depends on `knife` to run commands for it, and requires the `chef` gem for validating cookbook metadata. Infrastructure files must end in `.rb`, `.json` or `.yml` to be processed.
14
+ Spiceweasel currently depends on `knife` to run commands for it, and requires the `chef` gem for validating cookbook metadata. [Berkshelf](https://berkshelf.com) is a dependency for the Cookbook `Berksfile` support. Infrastructure files must end in `.rb`, `.json` or `.yml` to be processed.
15
15
 
16
- Written and tested initially with Chef 0.9.12 (may still work) and continuing development with the 10.x series. It is tested with Ruby 1.9.3. Version 2.0 was the last version known to work with Ruby 1.8.7 due to the introduction of the Berkshelf dependency in 2.1.
16
+ Written and tested with the Chef 11.x series (previous versions of Chef may still work). It is tested with Ruby 1.9.3. Version 2.0 was the last version known to work with Ruby 1.8.7 due to the introduction of the Berkshelf dependency in 2.1.
17
17
 
18
18
  # File Syntax #
19
19
 
@@ -66,10 +66,10 @@ knife cookbook upload mysql ntp
66
66
 
67
67
  ## Berkshelf ##
68
68
 
69
- If you prefer to use Berkshelf for managing your cookbooks, Spiceweasel supports adding `berksfile:` and the ability to specify the path and any Berkshelf options. You may mix use of `berkshelf:` with `cookbooks:` if you wish as well. Berkshelf-managed cookbooks will be included in the validation of roles, environments and run lists as well. You may simply use the YAML snippet
69
+ If you prefer to use Berkshelf for managing your cookbooks, Spiceweasel supports adding `berksfile:` and the ability to specify the path and any Berkshelf options. You may mix use of `berksfile:` with `cookbooks:` if you wish as well. Berkshelf-managed cookbooks will be included in the validation of roles, environments and run lists as well. You may simply use the YAML snippet
70
70
 
71
71
  ``` yaml
72
- berkshelf:
72
+ berksfile:
73
73
  ```
74
74
 
75
75
  which produces the command
@@ -94,16 +94,16 @@ berks upload --skip_syntax_check --config some_config.json -b /Users/mray/ws/lab
94
94
 
95
95
  ## Environments ##
96
96
 
97
- 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
97
+ 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. You may also pass a wildcard as an entry to load all matching environments (ie. `"*"` or `prod*"`). Validation is done to ensure the filename matches the environment and that any cookbooks referenced are listed in the manifest. The example YAML snippet
98
98
 
99
99
  ``` yaml
100
100
  environments:
101
101
  - development:
102
102
  - qa:
103
- - production:
103
+ - "prod*":
104
104
  ```
105
105
 
106
- produces the knife commands
106
+ assuming the `production` environment exists, produces the knife commands
107
107
 
108
108
  ```
109
109
  knife environment from file development.rb qa.rb production.rb
@@ -111,25 +111,26 @@ knife environment from file development.rb qa.rb production.rb
111
111
 
112
112
  ## Roles ##
113
113
 
114
- 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
114
+ 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. You may also pass a wildcard as an entry to load all matching roles (ie. `"*"` or `data*"`). 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
115
115
 
116
116
  ``` yaml
117
117
  roles:
118
118
  - base:
119
+ - "data*":
119
120
  - iisserver:
120
121
  - monitoring:
121
122
  - webserver:
122
123
  ```
123
124
 
124
- produces the knife commands
125
+ assuming the `database1.json` and `database2.json` roles exist, this produces the knife commands
125
126
 
126
127
  ```
127
- knife role from file base.rb iisserver.rb monitoring.rb webserver.rb
128
+ knife role from file base.rb database1.json database2.json iisserver.rb monitoring.rb webserver.rb
128
129
  ```
129
130
 
130
- n## Data Bags ##
131
+ ## Data Bags ##
131
132
 
132
- 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 using the `secret: secret_key_filename` attribute. Validation is done to ensure the JSON is properly formatted, the id matches and any secret key files are in the correct locations. Assuming the presence of `dataA.json` and `dataB.json` in the `data_bags/data` directory, the YAML snippet
133
+ 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. `"*"` or `"data*"`). Encrypted data bags are supported by using the `secret: secret_key_filename` attribute. Validation is done to ensure the JSON is properly formatted, the id matches and any secret key files are in the correct locations. Assuming the presence of `dataA.json` and `dataB.json` in the `data_bags/data` directory, the YAML snippet
133
134
 
134
135
  ``` yaml
135
136
  data bags:
@@ -154,7 +155,7 @@ produces the knife commands
154
155
  knife data bag create users
155
156
  knife data bag from file users alice.json bob.json chuck.json
156
157
  knife data bag create data
157
- knife data bag from file data *.json
158
+ knife data bag from file data dataA.json dataB.json
158
159
  knife data bag create passwords
159
160
  knife data bag from file passwords mysql.json rabbitmq.json --secret-file secret_key_filename
160
161
  ```
@@ -205,7 +206,7 @@ nodes:
205
206
  - -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -N webserver{{n}}
206
207
  ```
207
208
 
208
- produces the following:
209
+ produces the following
209
210
 
210
211
  ```
211
212
  seq 3 | parallel -j 0 -v "knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -N webserver{} -r 'role[base],role[tc],role[users]'"
@@ -215,7 +216,7 @@ which generates nodes named "webserver1", "webserver2" and "webserver3".
215
216
 
216
217
  ## Clusters ##
217
218
 
218
- Clusters are not a type supported by Chef, this is a logical construct added by Spiceweasel to enable managing sets of infrastructure together. The `clusters` section is a special case of `nodes`, where each member of the named cluster in the manifest will be tagged to ensure that the entire cluster may be created in sync (refresh and delete coming soon). The node syntax is the same as that under `nodes`, the only addition is the cluster name.
219
+ Clusters are not a type supported by Chef, this is a logical construct added by Spiceweasel to enable managing sets of infrastructure together. The `clusters` section is a special case of `nodes`, where each member of the named cluster in the manifest will be put in the same environment to ensure that the entire cluster may be created in sync (refresh and delete coming soon). The node syntax is the same as that under `nodes`, the only addition is the cluster name.
219
220
 
220
221
  ```
221
222
  clusters:
@@ -231,17 +232,17 @@ clusters:
231
232
  produces the knife commands
232
233
 
233
234
  ```
234
- knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-8af0f326 -f m1.medium -j '{"tags":["amazon+rolemysql"]}' -r 'role[mysql]'
235
- knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -j '{"tags":["amazon+rolewebserverrecipemysqlclient"]}' -r 'role[webserver],recipe[mysql::client]'
236
- knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -j '{"tags":["amazon+rolewebserverrecipemysqlclient"]}' -r 'role[webserver],recipe[mysql::client]'
237
- knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -j '{"tags":["amazon+rolewebserverrecipemysqlclient"]}' -r 'role[webserver],recipe[mysql::client]'
235
+ knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-8af0f326 -f m1.medium -E amazon
236
+ knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -E amazon -r 'role[webserver],recipe[mysql::client]'
237
+ knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -E amazon -r 'role[webserver],recipe[mysql::client]'
238
+ knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -E amazon -r 'role[webserver],recipe[mysql::client]'
238
239
  ```
239
240
 
240
241
  Another use of `clusters` is with the `--cluster-file` option, which will allow the use of a different file to define the members of the cluster. If there are any `nodes` or `clusters` defined in the primary manifest file, they will be removed and the content of the `--cluster-file` will be used instead. This allows you to switch the target destination of infrastructure by picking different `--cluster-file` endpoints.
241
242
 
242
243
  # Extract #
243
244
 
244
- Spiceweasel may also be used to generate knife commands or Spiceweasel manifests in JSON or YAML.
245
+ Spiceweasel may be used to generate knife commands or Spiceweasel manifests in JSON or YAML from an existing Chef repository.
245
246
 
246
247
  ```
247
248
  spiceweasel --extractlocal
@@ -263,13 +264,13 @@ When run in your Chef repository generates YAML-formatted output that may be cap
263
264
  To parse a Spiceweasel manifest, run the following from your Chef repository directory:
264
265
 
265
266
  ```
266
- spiceweasel path/to/infrastructure.json
267
+ spiceweasel path/to/infrastructure.yml
267
268
  ```
268
269
 
269
- or
270
+ or to extract infrastructure
270
271
 
271
272
  ```
272
- spiceweasel path/to/infrastructure.yml
273
+ spiceweasel --extractlocal
273
274
  ```
274
275
 
275
276
  This will generate the knife commands to build the described infrastructure. Infrastructure manifest files must end in either `.json` or `.yml`.
@@ -336,7 +337,7 @@ Print the version of spiceweasel currently installed.
336
337
 
337
338
  Author: Matt Ray <matt@opscode.com>
338
339
 
339
- Copyright: 2011-2012 Opscode, Inc
340
+ Copyright: 2011-2013 Opscode, Inc
340
341
 
341
342
  Licensed under the Apache License, Version 2.0 (the "License");
342
343
  you may not use this file except in compliance with the License.
@@ -88,7 +88,7 @@ module Spiceweasel
88
88
 
89
89
  option :log_level,
90
90
  :short => "-l LEVEL",
91
- :long => "--log_level LEVEL",
91
+ :long => "--loglevel LEVEL",
92
92
  :description => "Set the log level (debug, info, warn, error, fatal)",
93
93
  :proc => lambda { |l| l.to_sym }
94
94
 
@@ -132,6 +132,16 @@ module Spiceweasel
132
132
  :proc => lambda { |v| puts "Spiceweasel: #{::Spiceweasel::VERSION}" },
133
133
  :exit => 0
134
134
 
135
+ option :cookbook_directory,
136
+ :short => '-C COOKBOOK_DIR',
137
+ :long => '--cookbook-dir COOKBOOK_DIR',
138
+ :description => 'Set cookbook directory. Specify multiple times for multiple directories.',
139
+ :proc => lambda { |v|
140
+ Spiceweasel::Config[:cookbook_dir] ||= []
141
+ Spiceweasel::Config[:cookbook_dir] << v
142
+ Spiceweasel::Config[:cookbook_dir].uniq!
143
+ }
144
+
135
145
  option :unique_id,
136
146
  :long => '--unique-id UID',
137
147
  :description => 'Unique ID generally used for ruby based configs'
@@ -149,24 +159,7 @@ module Spiceweasel
149
159
  end
150
160
  Spiceweasel::Log.debug("file manifest: #{manifest}")
151
161
 
152
- berksfile = Berksfile.new(manifest['berksfile']) if manifest.include?('berksfile')
153
- if berksfile
154
- cookbooks = Cookbooks.new(manifest['cookbooks'], berksfile.cookbook_list)
155
- create = berksfile.create + cookbooks.create
156
- delete = berksfile.delete + cookbooks.delete
157
- else
158
- cookbooks = Cookbooks.new(manifest['cookbooks'])
159
- create = cookbooks.create
160
- delete = cookbooks.delete
161
- end
162
- environments = Environments.new(manifest['environments'], cookbooks)
163
- roles = Roles.new(manifest['roles'], environments, cookbooks)
164
- data_bags = DataBags.new(manifest['data bags'])
165
- nodes = Nodes.new(manifest['nodes'], cookbooks, environments, roles)
166
- clusters = Clusters.new(manifest['clusters'], cookbooks, environments, roles)
167
-
168
- create += environments.create + roles.create + data_bags.create + nodes.create + clusters.create
169
- delete += environments.delete + roles.delete + data_bags.delete + nodes.delete + clusters.delete
162
+ create, delete = process_manifest(manifest)
170
163
 
171
164
  if Spiceweasel::Config[:extractjson]
172
165
  puts JSON.pretty_generate(manifest)
@@ -208,12 +201,23 @@ module Spiceweasel
208
201
  ARGV << "-h" if ARGV.empty?
209
202
  begin
210
203
  parse_options
204
+ # Load knife configuration if using knife config
205
+ require 'chef/knife'
206
+ knife = Chef::Knife.new
207
+ # Only log on error during startup
208
+ Chef::Config[:verbosity] = 0
209
+ Chef::Config[:log_level] = :error
211
210
  if @config[:knifeconfig]
211
+ knife.read_config_file(@config[:knifeconfig])
212
212
  Spiceweasel::Config[:knife_options] = " -c #{@config[:knifeconfig]} "
213
+ else
214
+ knife.configure_chef
213
215
  end
214
216
  if @config[:serverurl]
215
217
  Spiceweasel::Config[:knife_options] += "--server-url #{@config[:serverurl]} "
216
218
  end
219
+ # NOTE: Only set cookbook path via config if path unset
220
+ Spiceweasel::Config[:cookbook_dir] ||= Chef::Config[:cookbook_path]
217
221
  rescue OptionParser::InvalidOption => e
218
222
  STDERR.puts e.message
219
223
  puts opt_parser.to_s
@@ -222,9 +226,11 @@ module Spiceweasel
222
226
  end
223
227
 
224
228
  def configure_logging
225
- Spiceweasel::Log.init(Spiceweasel::Config[:log_location])
226
- Spiceweasel::Log.level = Spiceweasel::Config[:log_level]
227
- Spiceweasel::Log.level = :debug if Spiceweasel::Config[:debug]
229
+ [Spiceweasel::Log, Chef::Log].each do |log_klass|
230
+ log_klass.init(Spiceweasel::Config[:log_location])
231
+ log_klass.level = Spiceweasel::Config[:log_level]
232
+ log_klass.level = :debug if Spiceweasel::Config[:debug]
233
+ end
228
234
  end
229
235
 
230
236
  def parse_and_validate_input(file)
@@ -255,12 +261,35 @@ module Spiceweasel
255
261
  exit(-1)
256
262
  rescue Exception => e
257
263
  STDERR.puts "ERROR: Invalid or missing manifest .json, .rb, or .yml file provided."
258
- STDERR.puts "ERROR: #{e}"
259
- puts opt_parser.to_s
264
+ if(Spiceweasel::Config[:log_level].to_s == 'debug')
265
+ STDERR.puts "ERROR: #{e}\n#{e.backtrace.join("\n")}"
266
+ end
260
267
  exit(-1)
261
268
  end
262
269
  output
263
270
  end
264
271
 
272
+ def process_manifest(manifest)
273
+ berksfile = Berksfile.new(manifest['berksfile']) if manifest.include?('berksfile')
274
+ if berksfile
275
+ cookbooks = Cookbooks.new(manifest['cookbooks'], berksfile.cookbook_list)
276
+ create = berksfile.create + cookbooks.create
277
+ delete = berksfile.delete + cookbooks.delete
278
+ else
279
+ cookbooks = Cookbooks.new(manifest['cookbooks'])
280
+ create = cookbooks.create
281
+ delete = cookbooks.delete
282
+ end
283
+ environments = Environments.new(manifest['environments'], cookbooks)
284
+ roles = Roles.new(manifest['roles'], environments, cookbooks)
285
+ data_bags = DataBags.new(manifest['data bags'])
286
+ nodes = Nodes.new(manifest['nodes'], cookbooks, environments, roles)
287
+ clusters = Clusters.new(manifest['clusters'], cookbooks, environments, roles)
288
+
289
+ create += environments.create + roles.create + data_bags.create + nodes.create + clusters.create
290
+ delete += environments.delete + roles.delete + data_bags.delete + nodes.delete + clusters.delete
291
+ return create, delete
292
+ end
293
+
265
294
  end
266
295
  end
@@ -34,13 +34,19 @@ module Spiceweasel
34
34
  node_name = node.keys.first
35
35
  run_list = node[node_name]['run_list'] || ''
36
36
  options = node[node_name]['options'] || ''
37
- # cluster tag is the cluster name + runlist
38
- tag = " -j '{\"tags\":[\"#{cluster_name}+#{run_list.gsub(/[ ,\[\]:]/, '')}\"]}'"
39
- Spiceweasel::Log.debug("cluster: #{cluster_name}:#{node_name}:tag:#{tag}")
40
- #push the tag back on the options
41
- node[node_name]['options'] = options + tag
37
+ # cluster tag is the cluster name + runlist once tags are working for every plugin
38
+ # until then, we're going to override the Environment
39
+ if options =~ /-E/ #delete any Environment
40
+ env = options.split('-E')[1].split[0]
41
+ edel = "-E#{env}"
42
+ options[edel] = "" if options.include?(edel)
43
+ edel = "-E #{env}"
44
+ options[edel] = "" if options.include?(edel)
45
+ Spiceweasel::Log.warn("deleting specified Environment '#{env}' from cluster: '#{cluster_name}'")
46
+ end
47
+ #push the Environment back on the options
48
+ node[node_name]['options'] = options + " -E #{cluster_name}"
42
49
  end
43
- Spiceweasel::Log.debug("cluster2: '#{cluster_name}' '#{cluster[cluster_name]}'")
44
50
  # let's reuse the Nodes logic
45
51
  nodes = Spiceweasel::Nodes.new(cluster[cluster_name], cookbooks, environments, roles)
46
52
  @create.concat(nodes.create)
@@ -32,6 +32,8 @@ module Spiceweasel
32
32
  @dependencies = Array.new
33
33
  #validate each of the cookbooks specified in the manifest
34
34
  if cookbooks
35
+ @loader = Chef::CookbookLoader.new(Spiceweasel::Config[:cookbook_dir])
36
+ @loader.load_cookbooks
35
37
  Spiceweasel::Log.debug("cookbooks: #{cookbooks}")
36
38
 
37
39
  c_names = []
@@ -43,7 +45,8 @@ module Spiceweasel
43
45
  end
44
46
  Spiceweasel::Log.debug("cookbook: #{name} #{version} #{options}")
45
47
  if File.directory?("cookbooks")
46
- if File.directory?("cookbooks/#{name}") #TODO use the name from metadata
48
+
49
+ if @loader.cookbooks_by_name[name]
47
50
  validateMetadata(name,version) unless Spiceweasel::Config[:novalidation]
48
51
  else
49
52
  if Spiceweasel::Config[:siteinstall] #use knife cookbook site install
@@ -81,9 +84,7 @@ module Spiceweasel
81
84
  #check the metadata for versions and gather deps
82
85
  def validateMetadata(cookbook,version)
83
86
  #check metadata.rb for requested version
84
- metadata_file = "cookbooks/#{cookbook}/metadata.rb"
85
- metadata = Chef::Cookbook::Metadata.new
86
- metadata.from_file(metadata_file)
87
+ metadata = @loader.cookbooks_by_name[cookbook].metadata
87
88
  Spiceweasel::Log.debug("validateMetadata: #{cookbook} #{metadata.name} #{metadata.version}")
88
89
  # Should the cookbook directory match the name in the metadata?
89
90
  if metadata.name.empty?
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # Author:: Matt Ray (<matt@opscode.com>)
3
3
  #
4
- # Copyright:: 2011-2012, Opscode, Inc <legal@opscode.com>
4
+ # Copyright:: 2011-2013, Opscode, Inc <legal@opscode.com>
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -56,17 +56,23 @@ module Spiceweasel
56
56
  items.each do |item|
57
57
  Spiceweasel::Log.debug("data bag #{db} item: #{item}")
58
58
  if item =~ /\*/ #wildcard support, will fail if directory not present
59
- files = Dir.glob("data_bags/#{db}/*.json")
60
- items.concat(files.collect {|x| x[x.rindex('/')+1..-6]})
61
- Spiceweasel::Log.debug("found items '#{items}' for data bag: #{db}")
59
+ files = Dir.glob("data_bags/#{db}/#{item}")
60
+ #remove anything not ending in .json
61
+ files.delete_if {|x| !x.end_with?('.json')}
62
+ items.concat(files.collect {|x| x["data_bags/#{db}/".length..-6]})
63
+ Spiceweasel::Log.debug("found files '#{files}' for data bag: #{db} with wildcard #{item}")
62
64
  next
63
65
  end
64
66
  validateItem(db, item) unless Spiceweasel::Config[:novalidation]
65
67
  end
66
- if secret
67
- create_command("knife data bag#{Spiceweasel::Config[:knife_options]} from file #{db} #{items.join('.json ')}.json --secret-file #{secret}")
68
- else
69
- create_command("knife data bag#{Spiceweasel::Config[:knife_options]} from file #{db} #{items.join('.json ')}.json")
68
+ items.delete_if {|x| x.include?("*")} #remove wildcards
69
+ items.sort!.uniq!
70
+ unless items.empty?
71
+ if secret
72
+ create_command("knife data bag#{Spiceweasel::Config[:knife_options]} from file #{db} #{items.join('.json ')}.json --secret-file #{secret}")
73
+ else
74
+ create_command("knife data bag#{Spiceweasel::Config[:knife_options]} from file #{db} #{items.join('.json ')}.json")
75
+ end
70
76
  end
71
77
  end
72
78
  end
@@ -81,6 +87,9 @@ module Spiceweasel
81
87
  f = File.read("data_bags/#{db}/#{item}.json")
82
88
  itemfile = JSON.parse(f) #invalid JSON will throw a trace
83
89
  #validate the id matches the file name
90
+ if item =~ /\// #pull out directories
91
+ item = item.split('/').last
92
+ end
84
93
  if !item.eql?(itemfile['id'])
85
94
  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."
86
95
  exit(-1)
@@ -31,31 +31,43 @@ module Spiceweasel
31
31
  @environment_list = Array.new
32
32
  if environments
33
33
  Spiceweasel::Log.debug("environments: #{environments}")
34
+ flatenvs = environments.collect {|x| x.keys}.flatten
34
35
  envfiles = []
35
- environments.each do |env|
36
- name = env.keys[0]
37
- Spiceweasel::Log.debug("environment: #{name}")
36
+ flatenvs.each do |env|
37
+ Spiceweasel::Log.debug("environment: #{env}")
38
38
  if File.directory?("environments")
39
- validate(name, cookbooks) unless Spiceweasel::Config[:novalidation]
39
+ #expand wildcards and push into environments
40
+ if env =~ /\*/ #wildcard support
41
+ wildenvs = Dir.glob("environments/#{env}")
42
+ #remove anything not ending in .json or .rb
43
+ wildenvs.delete_if {|x| !x.end_with?(".rb", ".json")}
44
+ Spiceweasel::Log.debug("found environments '#{wildenvs}' for wildcard: #{env}")
45
+ flatenvs.concat(wildenvs.collect {|x| x[x.rindex('/')+1..x.rindex('.')-1]})
46
+ next
47
+ end
48
+ validate(env, cookbooks) unless Spiceweasel::Config[:novalidation]
40
49
  elsif !Spiceweasel::Config[:novalidation]
41
50
  STDERR.puts "'environments' directory not found, unable to validate or load environments"
42
51
  exit(-1)
43
52
  end
44
- if File.exists?("environments/#{name}.json")
45
- envfiles.push("#{name}.json")
53
+ if File.exists?("environments/#{env}.json")
54
+ envfiles.push("#{env}.json")
46
55
  else #assume no .json means they want .rb and catchall for misssing dir
47
- envfiles.push("#{name}.rb")
56
+ envfiles.push("#{env}.rb")
48
57
  end
49
- delete_command("knife environment#{Spiceweasel::Config[:knife_options]} delete #{name} -y")
50
- @environment_list.push(name)
58
+ delete_command("knife environment#{Spiceweasel::Config[:knife_options]} delete #{env} -y")
59
+ @environment_list.push(env)
51
60
  end
52
- create_command("knife environment#{Spiceweasel::Config[:knife_options]} from file #{envfiles.join(' ')}")
61
+ create_command("knife environment#{Spiceweasel::Config[:knife_options]} from file #{envfiles.uniq.sort.join(' ')}")
53
62
  end
54
63
  end
55
64
 
56
65
  #validate the content of the environment file
57
66
  def validate(environment, cookbooks)
58
67
  file = %W(environments/#{environment}.rb environments/#{environment}.json).detect{|f| File.exists?(f)}
68
+ if environment =~ /\// #pull out directories
69
+ environment = environment.split('/').last
70
+ end
59
71
  if file
60
72
  case file
61
73
  when /\.json$/
@@ -89,7 +89,7 @@ module Spiceweasel
89
89
 
90
90
  def self.resolve_cookbooks(berkshelf_cookbooks = {})
91
91
  require 'solve'
92
- loader = Chef::CookbookLoader.new('./cookbooks')
92
+ loader = Chef::CookbookLoader.new(Spiceweasel::Config[:cookbook_dir])
93
93
  loader.load_cookbooks
94
94
  books = loader.cookbooks_by_name
95
95
  graph = Solve::Graph.new
@@ -37,6 +37,15 @@ module Spiceweasel
37
37
  flatroles.each do |role|
38
38
  Spiceweasel::Log.debug("role: #{role}")
39
39
  if File.directory?("roles")
40
+ #expand wildcards and push into flatroles
41
+ if role =~ /\*/ #wildcard support
42
+ wildroles = Dir.glob("roles/#{role}")
43
+ #remove anything not ending in .json or .rb
44
+ wildroles.delete_if {|x| !x.end_with?(".rb", ".json")}
45
+ Spiceweasel::Log.debug("found roles '#{wildroles}' for wildcard: #{role}")
46
+ flatroles.concat(wildroles.collect {|x| x[x.rindex('/')+1..x.rindex('.')-1]})
47
+ next
48
+ end
40
49
  validate(role, environments, cookbooks, flatroles) unless Spiceweasel::Config[:novalidation]
41
50
  elsif !Spiceweasel::Config[:novalidation]
42
51
  STDERR.puts "ERROR: 'roles' directory not found, unable to validate or load roles"
@@ -50,7 +59,7 @@ module Spiceweasel
50
59
  delete_command("knife role#{Spiceweasel::Config[:knife_options]} delete #{role} -y")
51
60
  @role_list.push(role)
52
61
  end
53
- create_command("knife role#{Spiceweasel::Config[:knife_options]} from file #{rolefiles.join(' ')}")
62
+ create_command("knife role#{Spiceweasel::Config[:knife_options]} from file #{rolefiles.uniq.sort.join(' ')}")
54
63
  end
55
64
  end
56
65
 
@@ -58,7 +67,10 @@ module Spiceweasel
58
67
  def validate(role, environments, cookbooks, roles)
59
68
  #validate the role passed in match the name of either the .rb or .json
60
69
  file = %W(roles/#{role}.rb roles/#{role}.json).detect{|f| File.exists?(f)}
61
- if(file)
70
+ if role =~ /\// #pull out directories
71
+ role = role.split('/').last
72
+ end
73
+ if file
62
74
  case file
63
75
  when /\.json$/
64
76
  c_role = Chef::JSONCompat.from_json(IO.read(file))
@@ -17,5 +17,5 @@
17
17
  #
18
18
 
19
19
  module Spiceweasel
20
- VERSION = '2.1.2'
20
+ VERSION = '2.2.0'
21
21
  end
@@ -36,12 +36,11 @@ knife node bulk delete .* -y
36
36
  knife cookbook upload apache2
37
37
  knife cookbook upload apt --freeze
38
38
  knife cookbook upload mysql ntp
39
- knife environment from file development.rb qa.rb production.rb
39
+ knife environment from file development.rb production.rb qa.rb
40
40
  knife role from file base.rb iisserver.rb monitoring.rb webserver.rb
41
41
  knife data bag create users
42
42
  knife data bag from file users alice.json bob.json chuck.json
43
43
  knife data bag create data
44
- knife data bag from file data *.json
45
44
  knife data bag create passwords
46
45
  knife data bag from file passwords mysql.json rabbitmq.json --secret-file secret_key
47
46
  knife bootstrap serverA -i ~/.ssh/mray.pem -x user --sudo -r 'role[base]'
@@ -53,10 +52,10 @@ knife rackspace server create --image 49 --flavor 2 -N db3 -r 'recipe[mysql],rol
53
52
  knife bootstrap windows winrm winboxA -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]'
54
53
  knife bootstrap windows ssh winboxB -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]'
55
54
  knife bootstrap windows ssh winboxC -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]'
56
- knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-8af0f326 -f m1.medium -j '{"tags":["amazon+rolemysql"]}' -r 'role[mysql]'
57
- knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -j '{"tags":["amazon+rolewebserverrecipemysqlclient"]}' -r 'role[webserver],recipe[mysql::client]'
58
- knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -j '{"tags":["amazon+rolewebserverrecipemysqlclient"]}' -r 'role[webserver],recipe[mysql::client]'
59
- knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -j '{"tags":["amazon+rolewebserverrecipemysqlclient"]}' -r 'role[webserver],recipe[mysql::client]'
55
+ knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-8af0f326 -f m1.medium -E amazon -r 'role[mysql]'
56
+ knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -E amazon -r 'role[webserver],recipe[mysql::client]'
57
+ knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -E amazon -r 'role[webserver],recipe[mysql::client]'
58
+ knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7000f019 -f m1.small -E amazon -r 'role[webserver],recipe[mysql::client]'
60
59
  OUTPUT
61
60
 
62
61
  @spiceweasel_binary = File.join(File.dirname(__FILE__), *%w[.. .. bin spiceweasel])
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: 2.1.2
4
+ version: 2.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-05 00:00:00.000000000 Z
12
+ date: 2013-05-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json