spiceweasel 2.0.1 → 2.1.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 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.
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.
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,13 +11,13 @@ 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 either end in `.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. 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.8.7 and 1.9.2.
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.
17
17
 
18
18
  # File Syntax #
19
19
 
20
- The syntax for the Spiceweasel file may be either JSON or YAML format of Chef primitives describing what is to be instantiated. Please refer to the [examples/example.json](https://github.com/mattray/spiceweasel/blob/master/examples/example.json) or [examples/example.yml](https://github.com/mattray/spiceweasel/blob/master/examples/example.yml) for examples of the same infrastructure. Each subsection below shows the YAML syntax converted to knife commands.
20
+ The syntax for the Spiceweasel file may be Ruby, JSON or YAML format of Chef primitives describing what is to be instantiated. Please refer to the [examples/example.json](https://github.com/mattray/spiceweasel/blob/master/examples/example.json) or [examples/example.yml](https://github.com/mattray/spiceweasel/blob/master/examples/example.yml) for examples of the same infrastructure. Each subsection below shows the YAML syntax converted to knife commands.
21
21
 
22
22
  ## Manifest syntax changes in Spiceweasel 2.0 ##
23
23
 
@@ -50,6 +50,7 @@ cookbooks:
50
50
  version: 1.2.0
51
51
  options: --freeze
52
52
  - mysql:
53
+ - ntp:
53
54
  ```
54
55
 
55
56
  produces the knife commands
@@ -60,7 +61,35 @@ knife cookbook site download apt 1.2.0 --file cookbooks/apt.tgz
60
61
  tar -C cookbooks/ -xf cookbooks/apt.tgz
61
62
  rm -f cookbooks/apt.tgz
62
63
  knife cookbook upload apt --freeze
63
- knife cookbook upload mysql
64
+ knife cookbook upload mysql ntp
65
+ ```
66
+
67
+ ## Berkshelf ##
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
70
+
71
+ ``` yaml
72
+ berkshelf:
73
+ ```
74
+
75
+ which produces the command
76
+
77
+ ```
78
+ berks upload -b ./Berksfile
79
+ ```
80
+
81
+ or you may use additional options like
82
+
83
+ ``` yaml
84
+ berksfile:
85
+ path: '/Users/mray/ws/lab-repo/Berksfile'
86
+ options: '--skip_syntax_check --config some_config.json'
87
+ ```
88
+
89
+ which produces the output
90
+
91
+ ```
92
+ berks upload --skip_syntax_check --config some_config.json -b /Users/mray/ws/lab-repo/Berksfile
64
93
  ```
65
94
 
66
95
  ## Environments ##
@@ -77,9 +106,7 @@ environments:
77
106
  produces the knife commands
78
107
 
79
108
  ```
80
- knife environment from file development.rb
81
- knife environment from file qa.rb
82
- knife environment from file production.rb
109
+ knife environment from file development.rb qa.rb production.rb
83
110
  ```
84
111
 
85
112
  ## Roles ##
@@ -97,10 +124,7 @@ roles:
97
124
  produces the knife commands
98
125
 
99
126
  ```
100
- knife role from file base.rb
101
- knife role from file iisserver.rb
102
- knife role from file monitoring.rb
103
- knife role from file webserver.rb
127
+ knife role from file base.rb iisserver.rb monitoring.rb webserver.rb
104
128
  ```
105
129
 
106
130
  n## Data Bags ##
@@ -128,15 +152,11 @@ produces the knife commands
128
152
 
129
153
  ```
130
154
  knife data bag create users
131
- knife data bag from file users alice.json
132
- knife data bag from file users bob.json
133
- knife data bag from file users chuck.json
155
+ knife data bag from file users alice.json bob.json chuck.json
134
156
  knife data bag create data
135
- knife data bag from file data dataA.json
136
- knife data bag from file data dataB.json
157
+ knife data bag from file data *.json
137
158
  knife data bag create passwords
138
- knife data bag from file passwords mysql.json --secret-file secret_key_filename
139
- knife data bag from file passwords rabbitmq.json --secret-file secret_key_filename
159
+ knife data bag from file passwords mysql.json rabbitmq.json --secret-file secret_key_filename
140
160
  ```
141
161
 
142
162
  ## Nodes ##
@@ -0,0 +1,59 @@
1
+ #
2
+ # Author:: Matt Ray (<matt@opscode.com>)
3
+ #
4
+ # Copyright:: 2013, 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
+ module Spiceweasel
20
+ class Berksfile
21
+
22
+ attr_reader :create
23
+ attr_reader :delete
24
+ attr_reader :cookbook_list
25
+
26
+ include CommandHelper
27
+
28
+ def initialize(berkshelf=nil)
29
+ @create = []
30
+ @delete = []
31
+ @cookbook_list = {}
32
+ # only load berkshelf if we are going to use it
33
+ require 'berkshelf'
34
+ berks_options = []
35
+ case berkshelf
36
+ when String
37
+ path = berkshelf
38
+ when Hash
39
+ path = berkshelf['path']
40
+ berks_options << berkshelf['options'] if berkshelf['options']
41
+ end
42
+ path ||= './Berksfile'
43
+ berks_options << "-b #{path}"
44
+ berks_options = berks_options.join(' ')
45
+ opts = Thor::Options.split(berks_options.split(' ')).last
46
+ resolve_opts = Thor::Options.new(Berkshelf::Cli.tasks['upload'].options).parse(opts)
47
+ berks = Berkshelf::Berksfile.from_file(path)
48
+ create_command("berks upload #{berks_options}")
49
+ Berkshelf.ui.mute do
50
+ Spiceweasel::Log.debug("berkshelf resolving dependencies: #{resolve_opts}")
51
+ berks.resolve(resolve_opts).each do |cb|
52
+ @cookbook_list[cb.cookbook_name] = cb.version
53
+ delete_command("knife cookbook#{Spiceweasel::Config[:knife_options]} delete #{cb.cookbook_name} #{cb.version} -a -y")
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -21,7 +21,9 @@ require 'json'
21
21
  require 'yaml'
22
22
 
23
23
  require 'spiceweasel'
24
+ require 'spiceweasel/command_helper'
24
25
  require 'spiceweasel/cookbooks'
26
+ require 'spiceweasel/berksfile'
25
27
  require 'spiceweasel/environments'
26
28
  require 'spiceweasel/roles'
27
29
  require 'spiceweasel/data_bags'
@@ -130,6 +132,10 @@ module Spiceweasel
130
132
  :proc => lambda { |v| puts "Spiceweasel: #{::Spiceweasel::VERSION}" },
131
133
  :exit => 0
132
134
 
135
+ option :unique_id,
136
+ :long => '--unique-id UID',
137
+ :description => 'Unique ID generally used for ruby based configs'
138
+
133
139
  def run
134
140
  if Spiceweasel::Config[:extractlocal] || Spiceweasel::Config[:extractjson] || Spiceweasel::Config[:extractyaml]
135
141
  manifest = Spiceweasel::ExtractLocal.parse_objects
@@ -143,19 +149,24 @@ module Spiceweasel
143
149
  end
144
150
  Spiceweasel::Log.debug("file manifest: #{manifest}")
145
151
 
146
- cookbooks = Cookbooks.new(manifest['cookbooks'])
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
147
162
  environments = Environments.new(manifest['environments'], cookbooks)
148
163
  roles = Roles.new(manifest['roles'], environments, cookbooks)
149
164
  data_bags = DataBags.new(manifest['data bags'])
150
165
  nodes = Nodes.new(manifest['nodes'], cookbooks, environments, roles)
151
166
  clusters = Clusters.new(manifest['clusters'], cookbooks, environments, roles)
152
167
 
153
- create = cookbooks.create + environments.create + roles.create + data_bags.create + nodes.create + clusters.create
154
- delete = cookbooks.delete + environments.delete + roles.delete + data_bags.delete + nodes.delete + clusters.delete
155
-
156
- #trim trailing whitespace
157
- create.each {|x| x.rstrip!}
158
- delete.each {|x| x.rstrip!}
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
159
170
 
160
171
  if Spiceweasel::Config[:extractjson]
161
172
  puts JSON.pretty_generate(manifest)
@@ -227,8 +238,11 @@ module Spiceweasel
227
238
  output = YAML.load_file(file)
228
239
  elsif (file.end_with?(".json"))
229
240
  output = JSON.parse(File.read(file))
241
+ elsif (file.end_with?(".rb"))
242
+ output = self.instance_eval(IO.read(file), file, 1)
243
+ output = JSON.parse(JSON.dump(output))
230
244
  else
231
- STDERR.puts "ERROR: #{file} is an unknown file type, please use a file ending with either '.json' or '.yml'."
245
+ STDERR.puts "ERROR: #{file} is an unknown file type, please use a file ending with '.rb', '.json' or '.yml'."
232
246
  exit(-1)
233
247
  end
234
248
  rescue Psych::SyntaxError => e
@@ -239,8 +253,9 @@ module Spiceweasel
239
253
  STDERR.puts e.message
240
254
  STDERR.puts "ERROR: Parsing error in #{file}."
241
255
  exit(-1)
242
- rescue Exception
243
- STDERR.puts "ERROR: No manifest .json or .yml file provided."
256
+ rescue Exception => e
257
+ STDERR.puts "ERROR: Invalid or missing manifest .json, .rb, or .yml file provided."
258
+ STDERR.puts "ERROR: #{e}"
244
259
  puts opt_parser.to_s
245
260
  exit(-1)
246
261
  end
@@ -0,0 +1,25 @@
1
+ module Spiceweasel
2
+ class Command
3
+
4
+ attr_reader :allow_failure
5
+ attr_reader :timeout
6
+ attr_reader :command
7
+
8
+ def initialize(command, options={})
9
+ @command = command.rstrip
10
+ @options = options
11
+ @timeout = options['timeout']
12
+ @allow_failure = options.has_key?('allow_failure') ? options['allow_failure'] : true
13
+ end
14
+
15
+ def shellout_opts
16
+ opts = {}
17
+ opts[:timeout] = timeout if timeout
18
+ opts
19
+ end
20
+
21
+ alias_method :allow_failure?, :allow_failure
22
+ alias_method :to_s, :command
23
+
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ require 'spiceweasel/command'
2
+
3
+ module Spiceweasel
4
+ module CommandHelper
5
+ def create_command(*args)
6
+ @create ||= []
7
+ @create.push(Command.new(*args))
8
+ end
9
+
10
+ def delete_command(*args)
11
+ @delete ||= []
12
+ @delete.push(Command.new(*args))
13
+ end
14
+ end
15
+ end
@@ -21,16 +21,20 @@ require 'chef/cookbook/metadata'
21
21
  module Spiceweasel
22
22
  class Cookbooks
23
23
 
24
+ include CommandHelper
25
+
24
26
  attr_reader :cookbook_list, :create, :delete
25
27
 
26
- def initialize(cookbooks = [])
28
+ def initialize(cookbooks = [], other_cookbook_list = {})
27
29
  @create = Array.new
28
30
  @delete = Array.new
29
- @cookbook_list = Hash.new
31
+ @cookbook_list = other_cookbook_list
30
32
  @dependencies = Array.new
31
33
  #validate each of the cookbooks specified in the manifest
32
34
  if cookbooks
33
35
  Spiceweasel::Log.debug("cookbooks: #{cookbooks}")
36
+
37
+ c_names = []
34
38
  cookbooks.each do |cookbook|
35
39
  name = cookbook.keys.first
36
40
  if cookbook[name]
@@ -43,21 +47,33 @@ module Spiceweasel
43
47
  validateMetadata(name,version) unless Spiceweasel::Config[:novalidation]
44
48
  else
45
49
  if Spiceweasel::Config[:siteinstall] #use knife cookbook site install
46
- @create.push("knife cookbook#{Spiceweasel::Config[:knife_options]} site install #{name} #{version} #{options}")
50
+ create_command("knife cookbook#{Spiceweasel::Config[:knife_options]} site install #{name} #{version} #{options}")
47
51
  else #use knife cookbook site download, untar and then remove the tarball
48
- @create.push("knife cookbook#{Spiceweasel::Config[:knife_options]} site download #{name} #{version} --file cookbooks/#{name}.tgz #{options}")
49
- @create.push("tar -C cookbooks/ -xf cookbooks/#{name}.tgz")
50
- @create.push("rm -f cookbooks/#{name}.tgz")
52
+ create_command("knife cookbook#{Spiceweasel::Config[:knife_options]} site download #{name} #{version} --file cookbooks/#{name}.tgz #{options}")
53
+ create_command("tar -C cookbooks/ -xf cookbooks/#{name}.tgz")
54
+ create_command("rm -f cookbooks/#{name}.tgz")
51
55
  end
52
56
  end
53
57
  elsif !Spiceweasel::Config[:novalidation]
54
58
  STDERR.puts "ERROR: 'cookbooks' directory not found, unable to validate, download and load cookbooks"
55
59
  exit(-1)
56
60
  end
57
- @create.push("knife cookbook#{Spiceweasel::Config[:knife_options]} upload #{name} #{options}")
58
- @delete.push("knife cookbook#{Spiceweasel::Config[:knife_options]} delete #{name} #{version} -a -y")
61
+
62
+ if(options)
63
+ if !c_names.empty?
64
+ create_command("knife cookbook#{Spiceweasel::Config[:knife_options]} upload #{c_names.join(' ')}")
65
+ c_names = []
66
+ end
67
+ create_command("knife cookbook#{Spiceweasel::Config[:knife_options]} upload #{name} #{options}")
68
+ else
69
+ c_names.push(name)
70
+ end
71
+ delete_command("knife cookbook#{Spiceweasel::Config[:knife_options]} delete #{name} #{version} -a -y")
59
72
  @cookbook_list[name] = version #used for validation
60
73
  end
74
+ if !c_names.empty?
75
+ create_command("knife cookbook#{Spiceweasel::Config[:knife_options]} upload #{c_names.join(' ')}")
76
+ end
61
77
  validateDependencies() unless Spiceweasel::Config[:novalidation]
62
78
  end
63
79
  end
@@ -21,6 +21,8 @@ require 'json'
21
21
  module Spiceweasel
22
22
  class DataBags
23
23
 
24
+ include CommandHelper
25
+
24
26
  attr_reader :create, :delete
25
27
 
26
28
  def initialize(data_bags = [])
@@ -39,8 +41,8 @@ module Spiceweasel
39
41
  STDERR.puts "ERROR: 'data_bags/#{db}' directory not found, unable to validate or load data bag items"
40
42
  exit(-1)
41
43
  end
42
- @create.push("knife data bag#{Spiceweasel::Config[:knife_options]} create #{db}")
43
- @delete.push("knife data bag#{Spiceweasel::Config[:knife_options]} delete #{db} -y")
44
+ create_command("knife data bag#{Spiceweasel::Config[:knife_options]} create #{db}")
45
+ delete_command("knife data bag#{Spiceweasel::Config[:knife_options]} delete #{db} -y")
44
46
  if data_bag[db]
45
47
  items = data_bag[db]['items']
46
48
  secret = data_bag[db]['secret']
@@ -60,11 +62,11 @@ module Spiceweasel
60
62
  next
61
63
  end
62
64
  validateItem(db, item) unless Spiceweasel::Config[:novalidation]
63
- if secret
64
- @create.push("knife data bag#{Spiceweasel::Config[:knife_options]} from file #{db} #{item}.json --secret-file #{secret}")
65
- else
66
- @create.push("knife data bag#{Spiceweasel::Config[:knife_options]} from file #{db} #{item}.json")
67
- end
65
+ 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
70
  end
69
71
  end
70
72
  end
@@ -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.
@@ -21,6 +21,8 @@ require 'json'
21
21
  module Spiceweasel
22
22
  class Environments
23
23
 
24
+ include CommandHelper
25
+
24
26
  attr_reader :environment_list, :create, :delete
25
27
 
26
28
  def initialize(environments = [], cookbooks = {})
@@ -29,6 +31,7 @@ module Spiceweasel
29
31
  @environment_list = Array.new
30
32
  if environments
31
33
  Spiceweasel::Log.debug("environments: #{environments}")
34
+ envfiles = []
32
35
  environments.each do |env|
33
36
  name = env.keys[0]
34
37
  Spiceweasel::Log.debug("environment: #{name}")
@@ -39,56 +42,38 @@ module Spiceweasel
39
42
  exit(-1)
40
43
  end
41
44
  if File.exists?("environments/#{name}.json")
42
- @create.push("knife environment#{Spiceweasel::Config[:knife_options]} from file #{name}.json")
45
+ envfiles.push("#{name}.json")
43
46
  else #assume no .json means they want .rb and catchall for misssing dir
44
- @create.push("knife environment#{Spiceweasel::Config[:knife_options]} from file #{name}.rb")
47
+ envfiles.push("#{name}.rb")
45
48
  end
46
- @delete.push("knife environment#{Spiceweasel::Config[:knife_options]} delete #{name} -y")
49
+ delete_command("knife environment#{Spiceweasel::Config[:knife_options]} delete #{name} -y")
47
50
  @environment_list.push(name)
48
51
  end
52
+ create_command("knife environment#{Spiceweasel::Config[:knife_options]} from file #{envfiles.join(' ')}")
49
53
  end
50
54
  end
51
55
 
52
56
  #validate the content of the environment file
53
57
  def validate(environment, cookbooks)
54
- #validate the environments passed in match the name of either the .rb or .json
55
- if File.exists?("environments/#{environment}.rb")
56
- #validate that the name inside the file matches
57
- name = File.open("environments/#{environment}.rb").grep(/^name/)[0].split()[1].gsub(/"/,'').to_s
58
- if !environment.eql?(name)
58
+ file = %W(environments/#{environment}.rb environments/#{environment}.json).detect{|f| File.exists?(f)}
59
+ if file
60
+ if (Chef::Version.new(Chef::VERSION) < Chef::Version.new('11.0.0'))
61
+ env = Chef::Environment.new(false)
62
+ else
63
+ env = Chef::Environment.new
64
+ end
65
+ env.from_file(file)
66
+ if(env.name != environment)
59
67
  STDERR.puts "ERROR: Environment '#{environment}' listed in the manifest does not match the name '#{name}' within the environments/#{environment}.rb file."
60
68
  exit(-1)
61
69
  end
62
- #validate the cookbooks exist if they're mentioned
63
- envcookbooks = File.open("environments/#{environment}.rb").grep(/^cookbook /)
64
- envcookbooks.each do |cb|
65
- dep = cb.split()[1].gsub(/"/,'').gsub(/,/,'')
70
+ env.cookbook_versions.keys.each do |dep|
66
71
  Spiceweasel::Log.debug("environment: '#{environment}' cookbook: '#{dep}'")
67
- if !cookbooks.member?(dep)
72
+ unless cookbooks.member?(dep)
68
73
  STDERR.puts "ERROR: Cookbook dependency '#{dep}' from environment '#{environment}' is missing from the list of cookbooks in the manifest."
69
74
  exit(-1)
70
75
  end
71
76
  end
72
- elsif File.exists?("environments/#{environment}.json")
73
- #load the json, don't symbolize since we don't need json_class
74
- f = File.read("environments/#{environment}.json")
75
- JSON.create_id = nil
76
- envfile = JSON.parse(f, {:symbolize_names => false})
77
- Spiceweasel::Log.debug("environment: '#{environment}' file: '#{envfile}'")
78
- #validate that the name inside the file matches
79
- Spiceweasel::Log.debug("environment: '#{environment}' name: '#{envfile['name']}'")
80
- if !environment.eql?(envfile['name'])
81
- STDERR.puts "ERROR: Environment '#{environment}' listed in the manifest does not match the name '#{envfile['name']}' within the 'environments/#{environment}.json' file."
82
- exit(-1)
83
- end
84
- #validate the cookbooks exist if they're mentioned
85
- envfile['cookbook_versions'].keys.each do |cb|
86
- Spiceweasel::Log.debug("environment: '#{environment}' cookbook: '#{cb}'")
87
- if !cookbooks.member?(cb.to_s)
88
- STDERR.puts "ERROR: Cookbook dependency '#{cb}' from environment '#{environment}' is missing from the list of cookbooks in the manifest."
89
- exit(-1)
90
- end
91
- end
92
77
  else #environment is not here
93
78
  STDERR.puts "ERROR: Invalid Environment '#{environment}' listed in the manifest but not found in the environments directory."
94
79
  exit(-1)
@@ -25,15 +25,14 @@ module Spiceweasel
25
25
  def initialize(commands)
26
26
  # for now we're shelling out
27
27
  commands.each do | cmd |
28
- knife = Mixlib::ShellOut.new(cmd)
28
+ knife = Mixlib::ShellOut.new(cmd.command, cmd.shellout_opts.merge(:live_stream => STDOUT))
29
29
  # check for parallel? and eventually use threads
30
30
  knife.run_command
31
- puts cmd
32
- puts knife.stdout
33
31
  puts knife.stderr
34
32
  Spiceweasel::Log.debug(cmd)
35
33
  Spiceweasel::Log.debug(knife.stdout)
36
34
  Spiceweasel::Log.fatal(knife.stderr) if !knife.stderr.empty?
35
+ find.error! unless cmd.allow_failure?
37
36
  end
38
37
  end
39
38
 
@@ -2,7 +2,7 @@
2
2
  # Author:: Geoff Meakin
3
3
  # Author:: Matt Ray (<matt@opscode.com>)
4
4
  #
5
- # Copyright:: 2012, Opscode, Inc <legal@opscode.com>
5
+ # Copyright:: 2012-2013, Opscode, Inc <legal@opscode.com>
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
8
8
  # you may not use this file except in compliance with the License.
@@ -17,27 +17,22 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
- require 'chef/cookbook/metadata'
20
+ require 'chef'
21
21
 
22
22
  module Spiceweasel
23
23
  class ExtractLocal
24
24
 
25
25
  def self.parse_objects
26
- objects = {'cookbooks' => nil, 'roles' => nil, 'environments' => nil, 'data bags' => nil, 'nodes' => nil}
26
+ objects = {}
27
27
 
28
- # COOKBOOKS
29
- cookbooks = Array.new
30
- Dir.glob('cookbooks/*').each do |cookbook_full_path|
31
- metadata = Chef::Cookbook::Metadata.new
32
- metadata.from_file("#{cookbook_full_path}/metadata.rb")
33
- if metadata.name.empty?
34
- Spiceweasel::Log.fatal("No cookbook name in the #{cookbook_full_path}/metadata.rb.")
35
- exit(-1)
36
- end
37
- Spiceweasel::Log.debug("dir_ext: #{metadata.name} #{metadata.version}")
38
- cookbooks.push(metadata)
28
+ # BERKSHELF
29
+ if File.file?('./Berksfile')
30
+ objects['berksfile'] = nil
31
+ berksfile = Berksfile.new(objects['berksfile'])
39
32
  end
40
- cookbooks = self.order_cookbooks_by_dependency(cookbooks)
33
+
34
+ # COOKBOOKS
35
+ cookbooks = self.resolve_cookbooks(berksfile.cookbook_list)
41
36
  objects['cookbooks'] = cookbooks unless cookbooks.empty?
42
37
 
43
38
  # ROLES
@@ -80,7 +75,6 @@ module Spiceweasel
80
75
  # nodes << {node => nil}
81
76
  # end
82
77
  # objects['nodes'] = nodes unless nodes.empty?
83
-
84
78
  objects
85
79
  end
86
80
 
@@ -92,39 +86,36 @@ module Spiceweasel
92
86
  name.join('.')
93
87
  end
94
88
 
95
- def self.order_cookbooks_by_dependency(cookbooks)
96
- # Weak algorithm, not particularly elegant, ignores version info as unlikely to have two versions of a cookbook anyway
97
- # We're going to find the cookbooks with their dependencies matched and keep going until all we have is unmatched deps
98
- sorted_cookbooks = Array.new
99
- unsorted_cookbooks = cookbooks
100
- scount = 0
101
- #keep looping until no more cookbooks are left or can't remove remainders
102
- while unsorted_cookbooks.any? and scount < cookbooks.length
103
- cookbook = unsorted_cookbooks.shift
104
- Spiceweasel::Log.debug("dir_ext: cookbook.dependencies: '#{cookbook.name}' #{cookbook.dependencies}")
105
- #if all the cookbook dependencies are in sorted_cookbooks
106
- if sorted_cookbooks.eql?(sorted_cookbooks | cookbook.dependencies.collect {|x| x[0]})
107
- sorted_cookbooks.push(cookbook.name)
108
- scount = 0
109
- else #put it back in the list
110
- unsorted_cookbooks.push(cookbook)
111
- scount = scount + 1
112
- end
113
- Spiceweasel::Log.debug("dir_ext: sorted_cookbooks: '#{sorted_cookbooks}' #{scount}")
89
+ def self.resolve_cookbooks(berkshelf_cookbooks = {})
90
+ require 'solve'
91
+ loader = Chef::CookbookLoader.new('./cookbooks')
92
+ loader.load_cookbooks
93
+ books = loader.cookbooks_by_name
94
+ graph = Solve::Graph.new
95
+ cblist = []
96
+ #push in the berkshelf cookbooks to cover any other deps
97
+ berkshelf_cookbooks.each do |name, version|
98
+ Spiceweasel::Log.debug("dir_ext:berks: #{name} #{version}")
99
+ graph.artifacts(name, version)
114
100
  end
115
- if scount > 0
116
- remainders = unsorted_cookbooks.collect {|x| x.name}
117
- Spiceweasel::Log.debug("dir_ext: remainders: '#{remainders}'")
118
- if Spiceweasel::Config[:novalidation] #stuff is missing, oh well
119
- sorted_cookbooks.push(remainders).flatten!
120
- else
121
- deps = unsorted_cookbooks.collect {|x| x.dependencies.collect {|x| x[0]} - sorted_cookbooks}
122
- STDERR.puts "ERROR: Dependencies not satisfied or circular dependencies in cookbook(s): #{remainders} depend(s) on #{deps}"
123
- exit(-1)
101
+ books.each do |name, cb|
102
+ Spiceweasel::Log.debug("dir_ext: #{name} #{cb.version}")
103
+ artifact = graph.artifacts(name, cb.version)
104
+ cblist.push([name, cb.version])
105
+ cb.metadata.dependencies.each do |dep_name, dep_version|
106
+ artifact.depends(dep_name, dep_version)
124
107
  end
125
108
  end
126
- #hack to get the format same as yaml/json parse
127
- sorted_cookbooks.collect { |x| { x => nil } }
109
+ #get the cookbooks and their versions, map to cookbook hash format
110
+ begin
111
+ cookbooks = []
112
+ Solve.it!(graph, cblist).each {|k,v| cookbooks.push({k => {'version' => v}})}
113
+ rescue Solve::Errors::NoSolutionError => e
114
+ STDERR.puts "ERROR: There are missing cookbook dependencies, please check your metadata.rb files."
115
+ exit(-1)
116
+ end
117
+ #remove any cookbooks managed by berkshelf
118
+ cookbooks.delete_if {|x| berkshelf_cookbooks.keys.member?(x.keys[0])}
128
119
  end
129
120
  end
130
121
  end
@@ -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.
@@ -19,13 +19,16 @@
19
19
  module Spiceweasel
20
20
  class Nodes
21
21
 
22
- PROVIDERS = %w{bluebox clodo cs ec2 gandi hp lxc openstack rackspace slicehost terremark voxel}
22
+ include CommandHelper
23
+
24
+ PROVIDERS = %w{bluebox clodo cs ec2 gandi hp lxc openstack rackspace slicehost terremark voxel vagrant}
23
25
 
24
26
  attr_reader :create, :delete
25
27
 
26
28
  def initialize(nodes, cookbooks, environments, roles)
27
29
  @create = Array.new
28
30
  @delete = Array.new
31
+ bulk_delete = false
29
32
  if nodes
30
33
  Spiceweasel::Log.debug("nodes: #{nodes}")
31
34
  nodes.each do |node|
@@ -40,6 +43,13 @@ module Spiceweasel
40
43
  options = node[name]['options'] || ''
41
44
  Spiceweasel::Log.debug("node: '#{name}' options: '#{options}'")
42
45
  validateOptions(name, options, environments) unless Spiceweasel::Config[:novalidation]
46
+ create_command_options = {}
47
+ %w(allow_create_failure timeout).each do |key|
48
+ if(node[name].has_key?(key))
49
+ create_command_options[key] = node[name][key]
50
+ end
51
+ end
52
+ additional_commands = node[name]['additional_commands'] || []
43
53
  end
44
54
  #provider support
45
55
  provider = name.split()
@@ -48,43 +58,60 @@ module Spiceweasel
48
58
  if provider.length == 2
49
59
  count = provider[1]
50
60
  end
61
+ provided_names = []
51
62
  if Spiceweasel::Config[:parallel]
52
63
  parallel = "seq #{count} | parallel -j 0 -v \""
53
64
  parallel += "knife #{provider[0]}#{Spiceweasel::Config[:knife_options]} server create #{options}".gsub(/\{\{n\}\}/, '{}')
54
65
  parallel += " -r '#{run_list}'" unless run_list.empty?
55
66
  parallel += "\""
56
- @create.push(parallel)
67
+ create_command(parallel, create_command_options)
57
68
  else
58
69
  count.to_i.times do |i|
59
70
  server = "knife #{provider[0]}#{Spiceweasel::Config[:knife_options]} server create #{options}".gsub(/\{\{n\}\}/, (i + 1).to_s)
60
71
  server += " -r '#{run_list}'" unless run_list.empty?
61
- @create.push(server)
72
+ provided_names << node[name]['name'].gsub('{{n}}', (i + 1).to_s) if node[name]['name']
73
+ create_command(server, create_command_options)
74
+ end
75
+ end
76
+ if provided_names.empty? && provider[0] != 'windows'
77
+ bulk_delete = true
78
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} list | xargs knife #{provider[0]} server delete -y")
79
+ else
80
+ provided_names.each do |p_name|
81
+ delete_command("knife #{provider[0]} server delete -y #{p_name}")
82
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{p_name} -y")
83
+ delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{p_name} -y")
62
84
  end
63
85
  end
64
- @delete.push("knife node#{Spiceweasel::Config[:knife_options]} list | xargs knife #{provider[0]} server delete -y")
65
86
  elsif name.start_with?("windows") #windows node bootstrap support
66
87
  nodeline = name.split()
67
88
  provider = nodeline.shift.split('_') #split on 'windows_ssh' etc
68
89
  nodeline.each do |server|
69
- server = "knife bootstrap #{provider[0]} #{provider[1]}#{Spiceweasel::Config[:knife_options]} #{server} #{options}"
70
- server += " -r '#{run_list}'" unless run_list.empty?
71
- @create.push(server)
72
- @delete.push("knife node#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
73
- @delete.push("knife client#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
90
+ servercommand = "knife bootstrap #{provider[0]} #{provider[1]}#{Spiceweasel::Config[:knife_options]} #{server} #{options}"
91
+ servercommand += " -r '#{run_list}'" unless run_list.empty?
92
+ create_command(servercommand, create_command_options)
93
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
94
+ delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
74
95
  end
75
- @delete.push("knife node#{Spiceweasel::Config[:knife_options]} list | xargs knife #{provider[0]} server delete -y")
76
96
  else #node bootstrap support
77
97
  name.split.each_with_index do |server, i|
78
- server = "knife bootstrap#{Spiceweasel::Config[:knife_options]} #{server} #{options}".gsub(/\{\{n\}\}/, (i + 1).to_s)
79
- server += " -r '#{run_list}'" unless run_list.empty?
80
- @create.push(server)
81
- @delete.push("knife node#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
82
- @delete.push("knife client#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
98
+ servercommand = "knife bootstrap#{Spiceweasel::Config[:knife_options]} #{server} #{options}".gsub(/\{\{n\}\}/, (i + 1).to_s)
99
+ servercommand += " -r '#{run_list}'" unless run_list.empty?
100
+ create_command(servercommand, create_command_options)
101
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
102
+ delete_command("knife client#{Spiceweasel::Config[:knife_options]} delete #{server} -y")
103
+ end
104
+ end
105
+ unless additional_commands.empty?
106
+ additional_commands.each do |cmd|
107
+ create_command(cmd, create_command_options)
83
108
  end
84
109
  end
85
110
  end
86
111
  end
87
- @delete.push("knife node#{Spiceweasel::Config[:knife_options]} bulk delete .* -y")
112
+ if bulk_delete
113
+ delete_command("knife node#{Spiceweasel::Config[:knife_options]} bulk delete .* -y")
114
+ end
88
115
  end
89
116
 
90
117
  #ensure run_list contents are listed previously.
@@ -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.
@@ -17,10 +17,13 @@
17
17
  #
18
18
 
19
19
  require 'json'
20
+ require 'chef'
20
21
 
21
22
  module Spiceweasel
22
23
  class Roles
23
24
 
25
+ include CommandHelper
26
+
24
27
  attr_reader :role_list, :create, :delete
25
28
 
26
29
  def initialize(roles = {}, environments = [], cookbooks = {})
@@ -30,6 +33,7 @@ module Spiceweasel
30
33
  if roles
31
34
  Spiceweasel::Log.debug("roles: #{roles}")
32
35
  flatroles = roles.collect {|x| x.keys}.flatten
36
+ rolefiles = []
33
37
  flatroles.each do |role|
34
38
  Spiceweasel::Log.debug("role: #{role}")
35
39
  if File.directory?("roles")
@@ -39,81 +43,51 @@ module Spiceweasel
39
43
  exit(-1)
40
44
  end
41
45
  if File.exists?("roles/#{role}.json")
42
- @create.push("knife role#{Spiceweasel::Config[:knife_options]} from file #{role}.json")
46
+ rolefiles.push("#{role}.json")
43
47
  else #assume no .json means they want .rb and catchall for misssing dir
44
- @create.push("knife role#{Spiceweasel::Config[:knife_options]} from file #{role}.rb")
48
+ rolefiles.push("#{role}.rb")
45
49
  end
46
- @delete.push("knife role#{Spiceweasel::Config[:knife_options]} delete #{role} -y")
50
+ delete_command("knife role#{Spiceweasel::Config[:knife_options]} delete #{role} -y")
47
51
  @role_list.push(role)
48
52
  end
53
+ create_command("knife role#{Spiceweasel::Config[:knife_options]} from file #{rolefiles.join(' ')}")
49
54
  end
50
55
  end
51
56
 
52
57
  #validate the content of the role file
53
58
  def validate(role, environments, cookbooks, roles)
54
59
  #validate the role passed in match the name of either the .rb or .json
55
- if File.exists?("roles/#{role}.rb")
56
- #validate that the name inside the file matches
57
- name = File.open("roles/#{role}.rb").grep(/^name/)[0].split()[1].gsub(/"/,'').to_s
58
- Spiceweasel::Log.debug("role: '#{role}' name: '#{name}'")
59
- if !role.eql?(name)
60
- STDERR.puts "ERROR: Role '#{role}' listed in the manifest does not match the name '#{name}' within the roles/#{role}.rb file."
61
- exit(-1)
60
+ file = %W(roles/#{role}.rb roles/#{role}.json).detect{|f| File.exists?(f)}
61
+ if(file)
62
+ if(Chef::Version.new(Chef::VERSION) < Chef::Version.new('11.0.0'))
63
+ c_role = Chef::Role.new(true)
64
+ else
65
+ c_role = Chef::Role.new
62
66
  end
63
- #grab any lines with 'recipe[' or 'role['
64
- rolerl = File.open("roles/#{role}.rb").grep(/recipe\[|role\[/)
65
- rolerl.each do |line|
66
- Spiceweasel::Log.debug("role: '#{role}' line: '#{line}'")
67
- line.strip.split(',').each do |rl|
68
- if rl =~ /recipe\[/ #it's a cookbook
69
- #split on the brackets and any colons
70
- dep = rl.split(/\[|\]/)[1].split(':')[0]
71
- Spiceweasel::Log.debug("role: '#{role}' cookbook: '#{rl}': dep: '#{dep}'")
72
- if !cookbooks.member?(dep)
73
- STDERR.puts "ERROR: Cookbook dependency '#{dep}' from role '#{role}' is missing from the list of cookbooks in the manifest."
74
- exit(-1)
75
- end
76
- elsif rl =~ /role\[/ #it's a role
77
- #split on the brackets
78
- dep = rl.split(/\[|\]/)[1]
79
- Spiceweasel::Log.debug("role: '#{role}' role: '#{rl}': dep: '#{dep}'")
80
- if !roles.member?(dep)
81
- STDERR.puts "ERROR: Role dependency '#{dep}' from role '#{role}' is missing from the list of roles in the manifest."
82
- exit(-1)
83
- end
84
- end
85
- end
86
- end
87
- #TODO validate any environment-specific runlists
88
- elsif File.exists?("roles/#{role}.json")
89
- #load the json, don't symbolize since we don't need json_class
90
- f = File.read("roles/#{role}.json")
91
- JSON.create_id = nil
92
- rolefile = JSON.parse(f, {:symbolize_names => false})
93
- #validate that the name inside the file matches
94
- Spiceweasel::Log.debug("role: '#{role}' name: '#{rolefile['name']}'")
95
- if !role.eql?(rolefile['name'])
96
- STDERR.puts "ERROR: Role '#{role}' listed in the manifest does not match the name '#{rolefile['name']}' within the 'roles/#{role}.json' file."
67
+ c_role.from_file(file)
68
+ Spiceweasel::Log.debug("role: '#{role}' name: '#{c_role.name}'")
69
+ if !role.eql?(c_role.name)
70
+ STDERR.puts "ERROR: Role '#{role}' listed in the manifest does not match the name '#{c_role.name}' within the roles/#{role}.rb file."
97
71
  exit(-1)
98
72
  end
99
- #validate the cookbooks and roles exist if they're mentioned in run_lists
100
- rolefile['run_list'].each do |rl|
101
- if rl =~ /recipe\[/ #it's a cookbook
102
- #split on the brackets and any colons
103
- dep = rl.split(/\[|\]/)[1].split(':')[0]
104
- Spiceweasel::Log.debug("role: '#{role}' cookbook: '#{rl}': dep: '#{dep}'")
105
- if !cookbooks.member?(dep)
106
- STDERR.puts "ERROR: Cookbook dependency '#{dep}' from role '#{role}' is missing from the list of cookbooks in the manifest."
73
+ c_role.run_list.each do |runlist_item|
74
+ if(runlist_item.recipe?)
75
+ Spiceweasel::Log.debug("recipe: #{runlist_item.name}")
76
+ cookbook,recipe = runlist_item.name.split('::')
77
+ Spiceweasel::Log.debug("role: '#{role}' cookbook: '#{cookbook}' dep: '#{runlist_item}'")
78
+ unless(cookbooks.member?(cookbook))
79
+ STDERR.puts "ERROR: Cookbook dependency '#{runlist_item}' from role '#{role}' is missing from the list of cookbooks in the manifest."
107
80
  exit(-1)
108
81
  end
109
- elsif rl =~ /role\[/ #it's a role
110
- #split on the brackets
111
- dep = rl.split(/\[|\]/)[1]
112
- Spiceweasel::Log.debug("role: '#{role}' role: '#{rl}': dep: '#{dep}'")
113
- if !roles.member?(dep)
114
- STDERR.puts "ERROR: Role dependency '#{dep}' from role '#{role}' is missing from the list of roles in the manifest."
82
+ elsif(runlist_item.role?)
83
+ Spiceweasel::Log.debug("role: '#{role}' role: '#{runlist_item}': dep: '#{runlist_item.name}'")
84
+ unless(roles.member?(runlist_item.name))
85
+ STDERR.puts "ERROR: Role dependency '#{runlist_item.name}' from role '#{role}' is missing from the list of roles in the manifest."
115
86
  exit(-1)
116
87
  end
88
+ else
89
+ STDERR.puts "ERROR: Unknown item in runlist: #{runlist_item.name}"
90
+ exit(-1)
117
91
  end
118
92
  end
119
93
  #TODO validate any environment-specific runlists
@@ -17,5 +17,5 @@
17
17
  #
18
18
 
19
19
  module Spiceweasel
20
- VERSION = '2.0.1'
20
+ VERSION = '2.1.0'
21
21
  end
@@ -5,6 +5,7 @@ describe 'The Spiceweasel binary' do
5
5
  knife cookbook delete apache2 -a -y
6
6
  knife cookbook delete apt 1.2.0 -a -y
7
7
  knife cookbook delete mysql -a -y
8
+ knife cookbook delete ntp -a -y
8
9
  knife environment delete development -y
9
10
  knife environment delete qa -y
10
11
  knife environment delete production -y
@@ -15,43 +16,34 @@ knife role delete webserver -y
15
16
  knife data bag delete users -y
16
17
  knife data bag delete data -y
17
18
  knife data bag delete passwords -y
18
- knife node delete knife bootstrap serverA -i ~/.ssh/mray.pem -x user --sudo -r 'role[base]' -y
19
- knife client delete knife bootstrap serverA -i ~/.ssh/mray.pem -x user --sudo -r 'role[base]' -y
20
- knife node delete knife bootstrap serverB -i ~/.ssh/mray.pem -x user --sudo -E production -r 'role[base]' -y
21
- knife client delete knife bootstrap serverB -i ~/.ssh/mray.pem -x user --sudo -E production -r 'role[base]' -y
22
- knife node delete knife bootstrap serverC -i ~/.ssh/mray.pem -x user --sudo -E production -r 'role[base]' -y
23
- knife client delete knife bootstrap serverC -i ~/.ssh/mray.pem -x user --sudo -E production -r 'role[base]' -y
19
+ knife node delete serverA -y
20
+ knife client delete serverA -y
21
+ knife node delete serverB -y
22
+ knife client delete serverB -y
23
+ knife node delete serverC -y
24
+ knife client delete serverC -y
24
25
  knife node list | xargs knife rackspace server delete -y
25
- knife node delete knife bootstrap windows winrm winboxA -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]' -y
26
- knife client delete knife bootstrap windows winrm winboxA -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]' -y
27
- knife node list | xargs knife windows server delete -y
28
- knife node delete knife bootstrap windows ssh winboxB -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]' -y
29
- knife client delete knife bootstrap windows ssh winboxB -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]' -y
30
- knife node delete knife bootstrap windows ssh winboxC -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]' -y
31
- knife client delete knife bootstrap windows ssh winboxC -x Administrator -P 'super_secret_password' -r 'role[base],role[iisserver]' -y
32
- knife node list | xargs knife windows server delete -y
26
+ knife node delete winboxA -y
27
+ knife client delete winboxA -y
28
+ knife node delete winboxB -y
29
+ knife client delete winboxB -y
30
+ knife node delete winboxC -y
31
+ knife client delete winboxC -y
33
32
  knife node bulk delete .* -y
34
33
  knife node list | xargs knife ec2 server delete -y
35
34
  knife node list | xargs knife ec2 server delete -y
36
35
  knife node bulk delete .* -y
37
36
  knife cookbook upload apache2
38
37
  knife cookbook upload apt --freeze
39
- knife cookbook upload mysql
40
- knife environment from file development.rb
41
- knife environment from file qa.rb
42
- knife environment from file production.rb
43
- knife role from file base.rb
44
- knife role from file iisserver.rb
45
- knife role from file monitoring.rb
46
- knife role from file webserver.rb
38
+ knife cookbook upload mysql ntp
39
+ knife environment from file development.rb qa.rb production.rb
40
+ knife role from file base.rb iisserver.rb monitoring.rb webserver.rb
47
41
  knife data bag create users
48
- knife data bag from file users alice.json
49
- knife data bag from file users bob.json
50
- knife data bag from file users chuck.json
42
+ knife data bag from file users alice.json bob.json chuck.json
51
43
  knife data bag create data
44
+ knife data bag from file data *.json
52
45
  knife data bag create passwords
53
- knife data bag from file passwords mysql.json --secret-file secret_key
54
- knife data bag from file passwords rabbitmq.json --secret-file secret_key
46
+ knife data bag from file passwords mysql.json rabbitmq.json --secret-file secret_key
55
47
  knife bootstrap serverA -i ~/.ssh/mray.pem -x user --sudo -r 'role[base]'
56
48
  knife bootstrap serverB -i ~/.ssh/mray.pem -x user --sudo -E production -r 'role[base]'
57
49
  knife bootstrap serverC -i ~/.ssh/mray.pem -x user --sudo -E production -r 'role[base]'
@@ -70,7 +62,15 @@ knife ec2 server create -S mray -i ~/.ssh/mray.pem -x ubuntu -G default -I ami-7
70
62
  @spiceweasel_binary = File.join(File.dirname(__FILE__), *%w[.. .. bin spiceweasel])
71
63
  end
72
64
 
73
- it "maintains consistent output from the example config" do
65
+ it "maintains consistent output from the example config with yml" do
74
66
  `#{@spiceweasel_binary} -r --novalidation examples/example.yml`.should == @expected_output
75
67
  end
68
+
69
+ it "maintains consistent output from the example config with json" do
70
+ `#{@spiceweasel_binary} -r --novalidation examples/example.json`.should == @expected_output
71
+ end
72
+
73
+ it "maintains consistent output from the example config with rb" do
74
+ `#{@spiceweasel_binary} -r --novalidation examples/example.rb`.should == @expected_output
75
+ end
76
76
  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: 2.0.1
4
+ version: 2.1.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-01-19 00:00:00.000000000 Z
12
+ date: 2013-03-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
@@ -107,6 +107,38 @@ dependencies:
107
107
  - - ! '>='
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: berkshelf
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: solve
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
110
142
  - !ruby/object:Gem::Dependency
111
143
  name: rspec
112
144
  requirement: !ruby/object:Gem::Requirement
@@ -135,8 +167,11 @@ files:
135
167
  - LICENSE
136
168
  - README.md
137
169
  - bin/spiceweasel
170
+ - lib/spiceweasel/berksfile.rb
138
171
  - lib/spiceweasel/cli.rb
139
172
  - lib/spiceweasel/clusters.rb
173
+ - lib/spiceweasel/command.rb
174
+ - lib/spiceweasel/command_helper.rb
140
175
  - lib/spiceweasel/config.rb
141
176
  - lib/spiceweasel/cookbooks.rb
142
177
  - lib/spiceweasel/data_bags.rb