taste_tester 0.0.14 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dbe88f9386cbb7c68b721ea057a30881dd98ba374a0e84528e43da739eb88d31
4
- data.tar.gz: 642d43ce2aa006331680178e0ab15afba8d88323ab89d2e1cb0321f74c663b07
3
+ metadata.gz: 7ba0813aa4a09d92549bb1555bef69e883a49b4ef292b9a2d7cc14dca77bce0e
4
+ data.tar.gz: 4a51383665bad129bba1e5e4d2e1d9df991eb021700ce6dc9ab1098fa8089ad5
5
5
  SHA512:
6
- metadata.gz: 3a256a6d7e67b346aad620a1782032394f68848ef9c085c24854b4d70ffdd718b64d4970c479b7080aba11b468423905759d0ae8f92d305aa38fe654187745b7
7
- data.tar.gz: '048097e573d03b748a08d09438820d9ee144c90d0fb43e0375fbd91e1354215d7c82d8a0b39f97452b0752ab8dc79fe5b5863c9108727ba87fc2810271f27ee8'
6
+ metadata.gz: 612005359c48c5cee20cb95e4c8880a087f1638887de099e0cd15dd99757b54a3e404643241289286c7d33ff9f29ed25dcd64d9bcefcefc2a9b36dfe97fbb6f0
7
+ data.tar.gz: 99fae7d5975e0a9c05d1ca0e68c18c03a237eb4e4b17c8e870b16d71e8a434717a01d81dc0aad2b15b35ee1fbc292c3dad230c29ab92f362059f2d5285808de7
data/README.md CHANGED
@@ -25,6 +25,7 @@ Typical usage is:
25
25
 
26
26
  ```text
27
27
  vi cookbooks/... # Make your changes and commit locally
28
+ taste-tester impact # Check where your changes are used
28
29
  taste-tester test -s [host] # Put host in Taste Tester mode
29
30
  ssh root@[host] # Log in to host
30
31
  # Run chef and watch it break
@@ -62,6 +63,7 @@ you want to test on, i.e. SSH public/private keys, SSH certificates, Kerberos
62
63
  * Colorize
63
64
  * BetweenMeals
64
65
  * Minitar
66
+ * Chef
65
67
 
66
68
  ## Automatic Untesting
67
69
 
@@ -91,7 +93,8 @@ All command-line options are available in the config file:
91
93
  * plugin_path (string, no default)
92
94
  * repo (string, default: `#{ENV['HOME']}/ops`)
93
95
  * testing_time (int, default: `3600`)
94
- * chef_client_command (strng, default: `chef-client`)
96
+ * chef_client_command (string, default: `chef-client`)
97
+ * json (bool, default: `false`)
95
98
  * skip_repo_checks (bool, default: `false`)
96
99
  * skip_pre_upload_hook (bool, default: `false`)
97
100
  * skip_post_upload_hook (bool, default: `false`)
@@ -112,7 +115,12 @@ The following options are also available:
112
115
  * checksum_dir - The checksum directory to put in knife.conf for users. Default:
113
116
  `#{ENV['HOME']}/.chef/checksums`
114
117
  * bundle - use a single tar.gz file for transporting cookbooks, roles and
115
- databags to clients. Experimental.
118
+ databags to clients. Experimental. Value is tri-state:
119
+ * `false` - server uses knife upload, client uses `chef_server`
120
+ * `:compatible` - make server support both methods, client uses tar.gz
121
+ * `true` - server only creates tar.gz, client uses tar.gz
122
+ Default: false
123
+ * impact - analyze local changes to determine which hosts/roles to test.
116
124
  Default: false
117
125
 
118
126
 
@@ -156,6 +164,23 @@ Stuff to do after putting all hosts in test mode.
156
164
 
157
165
  Additional checks you want to do on the repo as sanity checks.
158
166
 
167
+ * self.find_impact(changes)
168
+
169
+ Custom implementation of impact analysis. Uses `knife deps` by default. May
170
+ return any data structure, provided one or both of `self.post_impact` or
171
+ `self.print_impact` are defined.
172
+
173
+ * self.post_impact(basic_impact)
174
+
175
+ Stuff to do after preliminary impact analysis. May be used to extend the
176
+ information generated by `self.find_impact`, reformat the data structure, etc.
177
+
178
+ * self.print_impact(final_impact)
179
+
180
+ Custom output of calculated impact, useful if defining either of the other
181
+ impact hooks. Must return a truthy value to prevent the default output from
182
+ printing.
183
+
159
184
  **Plugin example**
160
185
 
161
186
  This is an example `/etc/taste-tester-plugin.rb` to add a user-defined string
@@ -170,7 +195,7 @@ Hooks.class_eval do
170
195
  end
171
196
  end
172
197
  ```
173
- Be sure to pass this plugin file with `-p` on the command line or set it as
198
+ Be sure to pass this plugin file with `-p` on the command line or set it as
174
199
  `plugin_path` in your `taste-tester-config.rb` file.
175
200
 
176
201
  ## License
@@ -39,15 +39,6 @@ module TasteTester
39
39
  exit(1)
40
40
  end
41
41
 
42
- # Do an initial read of the config file if it's in the default place, so
43
- # that if people override chef_client_command the help message is correct.
44
- if File.exists?(File.expand_path(TasteTester::Config.config_file))
45
- TasteTester::Config.from_file(
46
- File.expand_path(TasteTester::Config.config_file),
47
- )
48
- end
49
-
50
- cmd = TasteTester::Config.chef_client_command
51
42
  description = <<-ENDOFDESCRIPTION
52
43
  Welcome to taste-tester!
53
44
 
@@ -57,16 +48,18 @@ TLDR; Most common usage is:
57
48
  vi cookbooks/... # Make your changes and commit locally
58
49
  taste-tester test -s [host] # Put host in test mode
59
50
  ssh root@[host] # Log on host
60
- #{format('%-28s', " #{cmd}")} # Run chef and watch it break
51
+ # Run chef and watch it break
61
52
  vi cookbooks/... # Fix your cookbooks
62
53
  taste-tester upload # Upload the diff
63
54
  ssh root@[host] # Log on host
64
- #{format('%-28s', " #{cmd}")} # Run chef and watch it succeed
55
+ # Run chef and watch it succeed
65
56
  <#{verify}>
66
57
  taste-tester untest -s [host] # Put host back in production
67
58
  # (optional - will revert itself after 1 hour)
68
59
 
69
- And you're done! See the above wiki page for more details.
60
+ And you're done!
61
+ Note: There may be site specific changes, e.g. instead of chef-client you may
62
+ use a wrapper. See local documentation for details.
70
63
 
71
64
  MODES:
72
65
  test
@@ -107,11 +100,6 @@ MODES:
107
100
  status
108
101
  Print out the state of the world.
109
102
 
110
- run
111
- Run #{cmd} on the machine specified by '-s' over SSH and print the output.
112
- NOTE!! This is #{'NOT'.red} a sufficient test, you must log onto the remote
113
- machine and verify the changes you are trying to make are actually present.
114
-
115
103
  stop
116
104
  You probably don't want this. It will shutdown the chef-zero server on
117
105
  your localhost.
@@ -305,6 +293,17 @@ MODES:
305
293
  options[:roles] = roles
306
294
  end
307
295
 
296
+ opts.on(
297
+ '-J', '--jumps JUMP',
298
+ 'Uses ssh\'s `ProxyJump` support to ssh across bastion/jump hosts. ' +
299
+ 'This is particularly useful in tunnel mode to test machines that ' +
300
+ 'your workstatation doesn\'t have direct access to. The format is ' +
301
+ 'the same as `ssh -J`: a comma-separated list of hosts to forward ' +
302
+ 'through.'
303
+ ) do |jumps|
304
+ options[:jumps] = jumps
305
+ end
306
+
308
307
  opts.on('--really', 'Really do link-only. DANGEROUS!') do |r|
309
308
  options[:really] = r
310
309
  end
@@ -430,8 +429,6 @@ MODES:
430
429
  TasteTester::Commands.test
431
430
  when :untest
432
431
  TasteTester::Commands.untest
433
- when :run
434
- TasteTester::Commands.runchef
435
432
  when :upload
436
433
  TasteTester::Commands.upload
437
434
  when :impact
@@ -20,6 +20,8 @@ require 'taste_tester/logging'
20
20
  require 'between_meals/repo'
21
21
  require 'between_meals/knife'
22
22
  require 'between_meals/changeset'
23
+ require 'chef/log'
24
+ require 'chef/cookbook/chefignore'
23
25
 
24
26
  module TasteTester
25
27
  # Client side upload functionality
@@ -130,6 +132,7 @@ module TasteTester
130
132
  def populate(stream, writer, path, destination)
131
133
  full_path = File.join(File.join(TasteTester::Config.repo, path))
132
134
  return unless File.directory?(full_path)
135
+ chefignores = Chef::Cookbook::Chefignore.new(full_path)
133
136
  # everything is relative to the repo dir. chdir makes handling all the
134
137
  # paths within this simpler
135
138
  Dir.chdir(full_path) do
@@ -141,7 +144,17 @@ module TasteTester
141
144
  # paths are enumerated as relative to the input path '.', so we get
142
145
  # './dir/file'. Stripping off the first two characters gives us a
143
146
  # a cleaner 'dir/file' path.
144
- name = File.join(destination, p[2..-1])
147
+ relative_name = p[2..-1]
148
+ # some exclusions are rooted relative to cookbooks which are one
149
+ # level below the directory included here. Strip off the first part
150
+ # of the path. e.g. `fb_cookbook/spec/foo.rb` would be `spec/foo.rb`
151
+ # which matches `spec/*`.
152
+ relative_name_minus_first = relative_name.split(
153
+ File::SEPARATOR,
154
+ )[1..-1].join(File::SEPARATOR)
155
+ next if chefignores.ignored?(relative_name) ||
156
+ chefignores.ignored?(relative_name_minus_first)
157
+ name = File.join(destination, relative_name)
145
158
  if File.directory?(p)
146
159
  # skip it. This also handles symlinks to directories which aren't
147
160
  # useful either.
@@ -206,11 +219,12 @@ module TasteTester
206
219
  end
207
220
 
208
221
  def full
222
+ logger.warn('Doing full upload')
209
223
  if TasteTester::Config.bundle
210
224
  bundle_upload
211
- return
225
+ # only leave early if true (strictly bundle mode only)
226
+ return if TasteTester::Config.bundle == true
212
227
  end
213
- logger.warn('Doing full upload')
214
228
  @knife.cookbook_upload_all
215
229
  @knife.role_upload_all
216
230
  @knife.databag_upload_all
@@ -220,7 +234,7 @@ module TasteTester
220
234
  if TasteTester::Config.bundle
221
235
  logger.info('No partial support for bundle mode, doing full upload')
222
236
  bundle_upload
223
- return
237
+ return if TasteTester::Config.bundle == true
224
238
  end
225
239
  logger.info('Doing differential upload from ' +
226
240
  @server.latest_uploaded_ref)
@@ -149,19 +149,6 @@ module TasteTester
149
149
  end
150
150
  end
151
151
 
152
- def self.runchef
153
- hosts = TasteTester::Config.servers
154
- unless hosts
155
- logger.warn('You must provide a hostname')
156
- exit(1)
157
- end
158
- server = TasteTester::Server.new
159
- hosts.each do |hostname|
160
- host = TasteTester::Host.new(hostname, server)
161
- host.run
162
- end
163
- end
164
-
165
152
  def self.keeptesting
166
153
  hosts = TasteTester::Config.servers
167
154
  unless hosts
@@ -226,16 +213,17 @@ module TasteTester
226
213
 
227
214
  changes = _find_changeset(repo)
228
215
 
229
- # Use Knife (or custom logic) to check the dependencies of each role
230
- # against the list of changes. `impacted_roles` will contian the set
231
- # of roles with direct or indirect (dependency) modifications.
232
- impacted_roles = TasteTester::Hooks.impact_find_roles(changes)
233
- impacted_roles ||= _find_roles(changes)
216
+ # Perform preliminary impact analysis. By default, use Knife to find
217
+ # the roles dependent on modified cookbooks. Custom logic may provide
218
+ # additional information by defining the find_impact plugin method.
219
+ basic_impact = TasteTester::Hooks.find_impact(changes)
220
+ basic_impact ||= _find_roles(changes)
234
221
 
235
222
  # Do any post processing required on the list of impacted roles, such
236
- # as looking up hostnames associated with each role.
237
- final_impact = TasteTester::Hooks.post_impact(impacted_roles)
238
- final_impact ||= impacted_roles
223
+ # as looking up hostnames associated with each role. By default, pass
224
+ # the preliminary results through unmodified.
225
+ final_impact = TasteTester::Hooks.post_impact(basic_impact)
226
+ final_impact ||= basic_impact
239
227
 
240
228
  # Print the calculated impact. If a print hook is defined that
241
229
  # returns true, then the default print function is skipped.
@@ -283,7 +271,7 @@ module TasteTester
283
271
  if TasteTester::Config.relative_cookbook_dirs.length > 1
284
272
  logger.error('Knife deps does not support multiple cookbook paths.')
285
273
  logger.error('Please flatten the cookbooks into a single directory' +
286
- ' or override the impact_find_roles function.')
274
+ ' or define the find_impact method in a local plugin.')
287
275
  exit(1)
288
276
  end
289
277
 
@@ -292,7 +280,9 @@ module TasteTester
292
280
  databags = Set.new(changes.databags)
293
281
 
294
282
  if cookbooks.empty? && roles.empty?
295
- logger.warn('No cookbooks or roles have been modified.')
283
+ unless TasteTester::Config.json
284
+ logger.warn('No cookbooks or roles have been modified.')
285
+ end
296
286
  return Set.new
297
287
  end
298
288
 
@@ -45,7 +45,6 @@ module TasteTester
45
45
  knife_config "#{ENV['HOME']}/.chef/knife-#{ENV['USER']}-taste-tester.rb"
46
46
  checksum_dir "#{ENV['HOME']}/.chef/checksums"
47
47
  skip_repo_checks false
48
- chef_client_command 'chef-client'
49
48
  testing_time 3600
50
49
  chef_port_range [5000, 5500]
51
50
  tunnel_port 4001
@@ -62,6 +61,7 @@ module TasteTester
62
61
  transport 'ssh'
63
62
  no_repo false
64
63
  json false
64
+ jumps nil
65
65
 
66
66
  # Start/End refs for calculating changes in the repo.
67
67
  # - start_ref should be the "master" commit of the repository
@@ -51,21 +51,21 @@ module TasteTester
51
51
  # Find the set of roles dependent on the changed files.
52
52
  # If returning something other than a set of roles, post_impact and/or
53
53
  # print_impact should be specified to handle the output.
54
- def self.impact_find_roles(_changes); end
54
+ def self.find_impact(_changes); end
55
55
 
56
56
  # Do stuff after we find impacted roles
57
- # This should return a Set object with the final impact. To return more
58
- # complex data, you must also provide a print_impact function which returns
59
- # true to override the default output.
57
+ # This should return a JSON serializable object with the final impact
58
+ # assessment. You will probably also want to define a print_impact method
59
+ # which returns true to override the default output logic.
60
60
  def self.post_impact(_impacted_roles); end
61
61
 
62
- # Customized the printed output of impact
62
+ # Customize the printed output of impact
63
63
  # If this method returns true, the default output will not be printed.
64
64
  def self.print_impact(_final_impact); end
65
65
 
66
66
  def self.get(file)
67
67
  path = File.expand_path(file)
68
- logger.warn("Loading plugin at #{path}")
68
+ logger.warn("Loading plugin at #{path}") unless TasteTester::Config.json
69
69
  unless File.exists?(path)
70
70
  logger.error('Plugin file not found')
71
71
  exit(1)
@@ -44,38 +44,6 @@ module TasteTester
44
44
  end
45
45
  end
46
46
 
47
- def runchef
48
- logger.warn("Running '#{TasteTester::Config.chef_client_command}' " +
49
- "on #{@name}")
50
- cmd = "#{TasteTester::Config.ssh_command} " +
51
- "#{TasteTester::Config.user}@#{@name} "
52
- if TasteTester::Config.user != 'root'
53
- cc = Base64.encode64(cmds).delete("\n")
54
- cmd += "\"echo '#{cc}' | base64 --decode | sudo bash -x\""
55
- else
56
- cmd += "\"#{cmds}\""
57
- end
58
- status = IO.popen(
59
- cmd,
60
- ) do |io|
61
- # rubocop:disable AssignmentInCondition
62
- while line = io.gets
63
- puts line.chomp!
64
- end
65
- # rubocop:enable AssignmentInCondition
66
- io.close
67
- $CHILD_STATUS.to_i
68
- end
69
- logger.warn("Finished #{TasteTester::Config.chef_client_command}" +
70
- " on #{@name} with status #{status}")
71
- if status.zero?
72
- msg = "#{TasteTester::Config.chef_client_command} was successful" +
73
- ' - please log to the host and confirm all the intended' +
74
- ' changes were made'
75
- logger.error msg.upcase
76
- end
77
- end
78
-
79
47
  def get_transport
80
48
  case TasteTester::Config.transport
81
49
  when 'locallink'
@@ -125,15 +93,14 @@ module TasteTester
125
93
  # we work out if we won or lost a race with another user.
126
94
  transport << we_testing
127
95
 
128
- transport.run
96
+ status, output = transport.run
129
97
 
130
- case transport.status
98
+ case status
131
99
  when 0
132
100
  # no problem, keep going.
133
101
  nil
134
102
  when 42
135
- fail TasteTester::Exceptions::AlreadyTestingError,
136
- transport.output.chomp
103
+ fail TasteTester::Exceptions::AlreadyTestingError, output.chomp
137
104
  else
138
105
  transport.error!
139
106
  end
@@ -22,8 +22,6 @@ module TasteTester
22
22
  include TasteTester::Logging
23
23
  include BetweenMeals::Util
24
24
 
25
- attr_reader :output, :status
26
-
27
25
  def initialize
28
26
  @host = 'localhost'
29
27
  @cmds = []
@@ -36,11 +34,11 @@ module TasteTester
36
34
  alias << add
37
35
 
38
36
  def run
39
- @status, @output = exec(cmd, logger)
37
+ exec(cmd, logger)
40
38
  end
41
39
 
42
40
  def run!
43
- @status, @output = exec!(cmd, logger)
41
+ exec!(cmd, logger)
44
42
  rescue StandardError => e
45
43
  logger.error(e.message)
46
44
  error!
@@ -22,8 +22,6 @@ module TasteTester
22
22
  include TasteTester::Logging
23
23
  include BetweenMeals::Util
24
24
 
25
- attr_reader :output, :status
26
-
27
25
  def initialize
28
26
  print_noop_warning
29
27
  @host = 'localhost'
@@ -48,7 +46,7 @@ module TasteTester
48
46
  alias << add
49
47
 
50
48
  def run
51
- @status, @output = run!
49
+ run!
52
50
  end
53
51
 
54
52
  def run!
@@ -65,10 +65,10 @@ module TasteTester
65
65
  # on all addresses - both v4 and v6. Note that on localhost, ::1 is
66
66
  # v6-only, so we default to 127.0.0.1 instead.
67
67
  if TasteTester::Config.use_ssh_tunnels
68
- @addr = '127.0.0.1'
68
+ @addrs = ['127.0.0.1']
69
69
  @host = 'localhost'
70
70
  else
71
- @addr = '::'
71
+ @addrs = ['::', '0.0.0.0']
72
72
  begin
73
73
  @host = TasteTester::Config.my_hostname || Socket.gethostname
74
74
  rescue StandardError
@@ -175,7 +175,8 @@ module TasteTester
175
175
  extend ::TasteTester::Windows
176
176
  start_win_chef_zero_server
177
177
  else
178
- cmd = +"#{chef_zero_path} --host #{@addr} --port #{@state.port} -d"
178
+ hostarg = @addrs.map { |addr| "--host #{addr}" }.join(' ')
179
+ cmd = +"#{chef_zero_path} #{hostarg} --port #{@state.port} -d"
179
180
  if TasteTester::Config.chef_zero_logging
180
181
  cmd << " --log-file #{@log_file}" +
181
182
  ' --log-level debug'
@@ -22,8 +22,6 @@ module TasteTester
22
22
  include TasteTester::Logging
23
23
  include BetweenMeals::Util
24
24
 
25
- attr_reader :output, :status
26
-
27
25
  def initialize(host, tunnel = false)
28
26
  @host = host
29
27
  @tunnel = tunnel
@@ -37,11 +35,11 @@ module TasteTester
37
35
  alias << add
38
36
 
39
37
  def run
40
- @status, @output = exec(cmd, logger)
38
+ exec(cmd, logger)
41
39
  end
42
40
 
43
41
  def run!
44
- @status, @output = exec!(cmd, logger)
42
+ exec!(cmd, logger)
45
43
  rescue StandardError => e
46
44
  logger.error(e.message)
47
45
  error!
@@ -53,7 +51,9 @@ SSH returned error while connecting to #{TasteTester::Config.user}@#{@host}
53
51
  The host might be broken or your SSH access is not working properly
54
52
  Try doing
55
53
  #{TasteTester::Config.ssh_command} -v #{TasteTester::Config.user}@#{@host}
56
- and come back once that works
54
+ to see if ssh connection is good.
55
+ If ssh works, add '-v' key to taste-tester to see the list of commands it's
56
+ trying to execute, and try to run them manually on destination host
57
57
  ERRORMESSAGE
58
58
  error.lines.each { |x| logger.error x.strip }
59
59
  fail TasteTester::Exceptions::SshError
@@ -66,7 +66,8 @@ and come back once that works
66
66
  logger.info("Will run: '#{cmd}' on #{@host}")
67
67
  end
68
68
  cmds = @cmds.join(' && ')
69
- cmd = "#{TasteTester::Config.ssh_command} " +
69
+ jumps = TasteTester::Config.jumps ? "-J #{TasteTester::Config.jumps}" : ''
70
+ cmd = "#{TasteTester::Config.ssh_command} #{jumps} " +
70
71
  '-T -o BatchMode=yes ' +
71
72
  "-o ConnectTimeout=#{TasteTester::Config.ssh_connect_timeout} " +
72
73
  "#{TasteTester::Config.user}@#{@host} "
@@ -91,7 +91,10 @@ module TasteTester
91
91
  end
92
92
 
93
93
  def bundle
94
- TasteTester::State.read(:bundle)
94
+ val = TasteTester::State.read(:bundle)
95
+ # promote value to symbol to match config value.
96
+ return :compatible if val == 'compatible'
97
+ val
95
98
  end
96
99
 
97
100
  def bundle=(bundle)
@@ -36,7 +36,7 @@ module TasteTester
36
36
  def run
37
37
  @port = TasteTester::Config.tunnel_port
38
38
  logger.info("Setting up tunnel on port #{@port}")
39
- @status, @output = exec!(cmd, logger)
39
+ exec!(cmd, logger)
40
40
  rescue StandardError => e
41
41
  logger.error "Failed bringing up ssh tunnel: #{e}"
42
42
  exit(1)
@@ -56,7 +56,8 @@ module TasteTester
56
56
  # In most cases the first request from chef was "breaking" the tunnel,
57
57
  # in a way that port was still open, but subsequent requests were hanging.
58
58
  # This is reproducible and should be looked into.
59
- cmd = "#{TasteTester::Config.ssh_command} " +
59
+ jumps = TasteTester::Config.jumps ? "-J #{TasteTester::Config.jumps}" : ''
60
+ cmd = "#{TasteTester::Config.ssh_command} #{jumps} " +
60
61
  "-o ConnectTimeout=#{TasteTester::Config.ssh_connect_timeout} " +
61
62
  '-T -o BatchMode=yes ' +
62
63
  '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ' +
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taste_tester
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phil Dibowitz
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-08-02 00:00:00.000000000 Z
12
+ date: 2019-09-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: between_meals
@@ -67,6 +67,20 @@ dependencies:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: chef
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
70
84
  - !ruby/object:Gem::Dependency
71
85
  name: chef-zero
72
86
  requirement: !ruby/object:Gem::Requirement