chef 11.12.8 → 11.14.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -2
  3. data/distro/common/html/_sources/index.txt +6 -0
  4. data/distro/common/html/_sources/knife_ssl_check.txt +41 -0
  5. data/distro/common/html/_sources/knife_ssl_fetch.txt +41 -0
  6. data/distro/common/html/_static/basic.css +2 -5
  7. data/distro/common/html/_static/doctools.js +5 -14
  8. data/distro/common/html/_static/jquery.js +2 -154
  9. data/distro/common/html/_static/pygments.css +2 -2
  10. data/distro/common/html/_static/searchtools.js +212 -150
  11. data/distro/common/html/_static/underscore.js +29 -21
  12. data/distro/common/html/_static/websupport.js +1 -1
  13. data/distro/common/html/ctl_chef_client.html +15 -18
  14. data/distro/common/html/ctl_chef_server.html +7 -7
  15. data/distro/common/html/ctl_chef_shell.html +6 -6
  16. data/distro/common/html/ctl_chef_solo.html +7 -8
  17. data/distro/common/html/index.html +34 -24
  18. data/distro/common/html/knife.html +23 -24
  19. data/distro/common/html/knife_bootstrap.html +13 -9
  20. data/distro/common/html/knife_client.html +10 -11
  21. data/distro/common/html/knife_common_options.html +6 -7
  22. data/distro/common/html/knife_configure.html +3 -4
  23. data/distro/common/html/knife_cookbook.html +18 -11
  24. data/distro/common/html/knife_cookbook_site.html +14 -14
  25. data/distro/common/html/knife_data_bag.html +24 -23
  26. data/distro/common/html/knife_delete.html +4 -5
  27. data/distro/common/html/knife_deps.html +4 -5
  28. data/distro/common/html/knife_diff.html +6 -7
  29. data/distro/common/html/knife_download.html +12 -13
  30. data/distro/common/html/knife_edit.html +4 -5
  31. data/distro/common/html/knife_environment.html +8 -9
  32. data/distro/common/html/knife_exec.html +9 -10
  33. data/distro/common/html/knife_index_rebuild.html +4 -5
  34. data/distro/common/html/knife_list.html +8 -9
  35. data/distro/common/html/knife_node.html +34 -33
  36. data/distro/common/html/knife_raw.html +2 -3
  37. data/distro/common/html/knife_recipe_list.html +3 -4
  38. data/distro/common/html/knife_role.html +30 -29
  39. data/distro/common/html/knife_search.html +7 -7
  40. data/distro/common/html/knife_show.html +4 -5
  41. data/distro/common/html/knife_ssh.html +2 -3
  42. data/distro/common/html/knife_ssl_check.html +148 -0
  43. data/distro/common/html/knife_ssl_fetch.html +152 -0
  44. data/distro/common/html/knife_status.html +4 -5
  45. data/distro/common/html/knife_tag.html +2 -3
  46. data/distro/common/html/knife_upload.html +5 -6
  47. data/distro/common/html/knife_user.html +9 -10
  48. data/distro/common/html/knife_using.html +12 -12
  49. data/distro/common/html/knife_xargs.html +11 -12
  50. data/distro/common/html/search.html +1 -2
  51. data/distro/common/html/searchindex.js +1 -1
  52. data/distro/common/man/man1/chef-shell.1 +19 -11
  53. data/distro/common/man/man1/knife-bootstrap.1 +35 -19
  54. data/distro/common/man/man1/knife-client.1 +111 -28
  55. data/distro/common/man/man1/knife-configure.1 +30 -14
  56. data/distro/common/man/man1/knife-cookbook-site.1 +105 -22
  57. data/distro/common/man/man1/knife-cookbook.1 +164 -23
  58. data/distro/common/man/man1/knife-data-bag.1 +157 -33
  59. data/distro/common/man/man1/knife-delete.1 +21 -17
  60. data/distro/common/man/man1/knife-deps.1 +60 -16
  61. data/distro/common/man/man1/knife-diff.1 +37 -17
  62. data/distro/common/man/man1/knife-download.1 +68 -24
  63. data/distro/common/man/man1/knife-edit.1 +19 -15
  64. data/distro/common/man/man1/knife-environment.1 +105 -17
  65. data/distro/common/man/man1/knife-exec.1 +78 -18
  66. data/distro/common/man/man1/knife-index-rebuild.1 +16 -8
  67. data/distro/common/man/man1/knife-list.1 +39 -23
  68. data/distro/common/man/man1/knife-node.1 +170 -22
  69. data/distro/common/man/man1/knife-raw.1 +33 -13
  70. data/distro/common/man/man1/knife-recipe-list.1 +17 -5
  71. data/distro/common/man/man1/knife-role.1 +86 -18
  72. data/distro/common/man/man1/knife-search.1 +80 -16
  73. data/distro/common/man/man1/knife-show.1 +30 -14
  74. data/distro/common/man/man1/knife-ssh.1 +54 -14
  75. data/distro/common/man/man1/knife-ssl-check.1 +207 -0
  76. data/distro/common/man/man1/knife-ssl-fetch.1 +207 -0
  77. data/distro/common/man/man1/knife-status.1 +48 -12
  78. data/distro/common/man/man1/knife-tag.1 +30 -10
  79. data/distro/common/man/man1/knife-upload.1 +72 -20
  80. data/distro/common/man/man1/knife-user.1 +79 -23
  81. data/distro/common/man/man1/knife-xargs.1 +61 -53
  82. data/distro/common/man/man8/chef-client.8 +87 -29
  83. data/distro/common/man/man8/chef-solo.8 +36 -15
  84. data/lib/chef/application.rb +19 -14
  85. data/lib/chef/application/client.rb +5 -0
  86. data/lib/chef/application/solo.rb +5 -0
  87. data/lib/chef/application/windows_service_manager.rb +3 -0
  88. data/lib/chef/chef_fs/chef_fs_data_store.rb +72 -24
  89. data/lib/chef/chef_fs/file_system/chef_repository_file_system_cookbook_dir.rb +20 -4
  90. data/lib/chef/chef_fs/file_system/chef_repository_file_system_cookbooks_dir.rb +20 -1
  91. data/lib/chef/chef_fs/file_system/file_system_entry.rb +10 -2
  92. data/lib/chef/client.rb +2 -3
  93. data/lib/chef/config.rb +34 -8
  94. data/lib/chef/cookbook/cookbook_version_loader.rb +45 -4
  95. data/lib/chef/cookbook_version.rb +38 -30
  96. data/lib/chef/dsl/recipe.rb +4 -1
  97. data/lib/chef/event_dispatch/base.rb +14 -0
  98. data/lib/chef/event_dispatch/events_output_stream.rb +29 -0
  99. data/lib/chef/exceptions.rb +8 -0
  100. data/lib/chef/formatters/base.rb +16 -45
  101. data/lib/chef/formatters/doc.rb +51 -26
  102. data/lib/chef/formatters/indentable_output_stream.rb +165 -0
  103. data/lib/chef/knife/node_environment_set.rb +54 -0
  104. data/lib/chef/knife/user_create.rb +1 -1
  105. data/lib/chef/monkey_patches/pathname.rb +32 -0
  106. data/lib/chef/node.rb +1 -1
  107. data/lib/chef/platform/provider_mapping.rb +345 -338
  108. data/lib/chef/policy_builder/expand_node_object.rb +1 -1
  109. data/lib/chef/policy_builder/policyfile.rb +1 -1
  110. data/lib/chef/provider.rb +1 -0
  111. data/lib/chef/provider/git.rb +1 -1
  112. data/lib/chef/provider/link.rb +2 -2
  113. data/lib/chef/provider/remote_file/content.rb +1 -1
  114. data/lib/chef/provider/remote_file/local_file.rb +8 -2
  115. data/lib/chef/provider/service/arch.rb +0 -1
  116. data/lib/chef/provider/service/debian.rb +0 -2
  117. data/lib/chef/provider/service/freebsd.rb +2 -1
  118. data/lib/chef/provider/service/gentoo.rb +1 -1
  119. data/lib/chef/provider/service/init.rb +0 -1
  120. data/lib/chef/provider/service/insserv.rb +0 -2
  121. data/lib/chef/provider/service/invokercd.rb +0 -2
  122. data/lib/chef/provider/service/macosx.rb +2 -1
  123. data/lib/chef/provider/service/redhat.rb +0 -1
  124. data/lib/chef/provider/service/simple.rb +1 -0
  125. data/lib/chef/provider/service/solaris.rb +1 -0
  126. data/lib/chef/provider/service/systemd.rb +1 -1
  127. data/lib/chef/provider/service/upstart.rb +1 -1
  128. data/lib/chef/provider/user.rb +9 -9
  129. data/lib/chef/provider/user/solaris.rb +2 -0
  130. data/lib/chef/resource.rb +1 -0
  131. data/lib/chef/resource/remote_file.rb +32 -6
  132. data/lib/chef/run_context.rb +22 -0
  133. data/lib/chef/run_lock.rb +43 -4
  134. data/lib/chef/version.rb +2 -2
  135. data/spec/functional/http/simple_spec.rb +84 -0
  136. data/spec/functional/resource/remote_file_spec.rb +107 -43
  137. data/spec/functional/rest_spec.rb +94 -0
  138. data/spec/functional/run_lock_spec.rb +1 -1
  139. data/spec/functional/win32/service_manager_spec.rb +6 -0
  140. data/spec/integration/knife/chef_fs_data_store_spec.rb +2 -0
  141. data/spec/integration/recipes/lwrp_inline_resources_spec.rb +76 -0
  142. data/spec/spec_helper.rb +2 -0
  143. data/spec/support/mock/platform.rb +7 -0
  144. data/spec/support/pedant/pedant_config.rb +121 -0
  145. data/spec/support/pedant/run_pedant.rb +63 -0
  146. data/spec/support/pedant/stickywicket.pem +27 -0
  147. data/spec/support/shared/functional/http.rb +242 -0
  148. data/spec/support/shared/unit/api_error_inspector.rb +2 -2
  149. data/spec/unit/api_client_spec.rb +2 -2
  150. data/spec/unit/application/client_spec.rb +6 -1
  151. data/spec/unit/application/knife_spec.rb +4 -0
  152. data/spec/unit/application/solo_spec.rb +2 -0
  153. data/spec/unit/application_spec.rb +7 -0
  154. data/spec/unit/client_spec.rb +16 -0
  155. data/spec/unit/config_spec.rb +3 -20
  156. data/spec/unit/cookbook_version_spec.rb +224 -122
  157. data/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb +2 -2
  158. data/spec/unit/formatters/error_inspectors/cookbook_resolve_error_inspector_spec.rb +2 -2
  159. data/spec/unit/formatters/error_inspectors/cookbook_sync_error_inspector_spec.rb +2 -2
  160. data/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb +2 -2
  161. data/spec/unit/formatters/error_inspectors/run_list_expansion_error_inspector_spec.rb +2 -2
  162. data/spec/unit/handler_spec.rb +0 -1
  163. data/spec/unit/knife/client_bulk_delete_spec.rb +3 -0
  164. data/spec/unit/knife/cookbook_bulk_delete_spec.rb +2 -0
  165. data/spec/unit/knife/cookbook_metadata_spec.rb +2 -2
  166. data/spec/unit/knife/cookbook_site_install_spec.rb +3 -1
  167. data/spec/unit/knife/cookbook_upload_spec.rb +10 -10
  168. data/spec/unit/knife/node_environment_set_spec.rb +80 -0
  169. data/spec/unit/knife/user_create_spec.rb +6 -4
  170. data/spec/unit/knife/user_edit_spec.rb +5 -0
  171. data/spec/unit/knife_spec.rb +3 -0
  172. data/spec/unit/mixin/securable_spec.rb +18 -20
  173. data/spec/unit/node/attribute_spec.rb +15 -2
  174. data/spec/unit/node/immutable_collections_spec.rb +4 -4
  175. data/spec/unit/provider/cron_spec.rb +14 -14
  176. data/spec/unit/provider/git_spec.rb +4 -4
  177. data/spec/unit/provider/group_spec.rb +1 -1
  178. data/spec/unit/provider/ohai_spec.rb +2 -2
  179. data/spec/unit/provider/remote_file/content_spec.rb +58 -35
  180. data/spec/unit/provider/remote_file/local_file_spec.rb +23 -0
  181. data/spec/unit/provider/service/solaris_smf_service_spec.rb +13 -13
  182. data/spec/unit/resource/mount_spec.rb +0 -1
  183. data/spec/unit/resource/remote_file_spec.rb +29 -0
  184. data/spec/unit/resource_spec.rb +1 -1
  185. data/spec/unit/run_context_spec.rb +7 -0
  186. data/spec/unit/run_lock_spec.rb +98 -0
  187. data/spec/unit/version_constraint_spec.rb +1 -1
  188. metadata +164 -151
  189. data/distro/common/html/_static/chef.css +0 -507
  190. data/distro/common/html/_static/chef_logo.png +0 -0
  191. data/lib/chef/checksum/storage.rb +0 -18
  192. data/lib/chef/checksum/storage/filesystem.rb +0 -56
  193. data/spec/unit/checksum/storage/filesystem_spec.rb +0 -70
@@ -277,6 +277,20 @@ class Chef
277
277
  def resource_updated(resource, action)
278
278
  end
279
279
 
280
+ # A stream has opened.
281
+ def stream_opened(stream, options = {})
282
+ end
283
+
284
+ # A stream has closed.
285
+ def stream_closed(stream, options = {})
286
+ end
287
+
288
+ # A chunk of data from a stream. The stream is managed by "stream," which
289
+ # can be any tag whatsoever. Data in different "streams" may not be placed
290
+ # on the same line or even sent to the same console.
291
+ def stream_output(stream, output, options = {})
292
+ end
293
+
280
294
  # Called before handlers run
281
295
  def handlers_start(handler_count)
282
296
  end
@@ -0,0 +1,29 @@
1
+ class Chef
2
+ module EventDispatch
3
+ class EventsOutputStream
4
+ # This is a fake stream that connects to events.
5
+ #
6
+ # == Arguments
7
+ # events: the EventDispatch object to send data to (run_context.events)
8
+ # options is a hash with these possible options:
9
+ # - name: a string that identifies the stream to the user. Preferably short.
10
+
11
+ def initialize(events, options = {})
12
+ @events = events
13
+ @options = options
14
+ events.stream_opened(self, options)
15
+ end
16
+
17
+ attr_reader :options
18
+ attr_reader :events
19
+
20
+ def print(str)
21
+ events.stream_output(self, str, options)
22
+ end
23
+
24
+ def close
25
+ events.stream_closed(self, options)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -321,5 +321,13 @@ class Chef
321
321
  super "This functionality is not supported on platform #{platform}."
322
322
  end
323
323
  end
324
+
325
+ # Raised when Chef::Config[:run_lock_timeout] is set and some other client run fails
326
+ # to release the run lock becure Chef::Config[:run_lock_timeout] seconds pass.
327
+ class RunLockTimeout < RuntimeError
328
+ def initialize(duration, blocking_pid)
329
+ super "Unable to acquire lock. Waited #{duration} seconds for #{blocking_pid} to release."
330
+ end
331
+ end
324
332
  end
325
333
  end
@@ -21,6 +21,7 @@ require 'chef/event_dispatch/base'
21
21
  require 'chef/formatters/error_inspectors'
22
22
  require 'chef/formatters/error_descriptor'
23
23
  require 'chef/formatters/error_mapper'
24
+ require 'chef/formatters/indentable_output_stream'
24
25
 
25
26
  class Chef
26
27
 
@@ -56,47 +57,6 @@ class Chef
56
57
  formatter_class.new(out, err)
57
58
  end
58
59
 
59
- # == Outputter
60
- # Handles basic printing tasks like colorizing.
61
- # --
62
- # TODO: Duplicates functionality from knife, upfactor.
63
- class Outputter
64
-
65
- attr_reader :out
66
- attr_reader :err
67
-
68
- def initialize(out, err)
69
- @out, @err = out, err
70
- end
71
-
72
- def highline
73
- @highline ||= begin
74
- require 'highline'
75
- HighLine.new
76
- end
77
- end
78
-
79
- def color(string, *colors)
80
- if Chef::Config[:color]
81
- @out.print highline.color(string, *colors)
82
- else
83
- @out.print string
84
- end
85
- end
86
-
87
- alias :print :color
88
-
89
- def puts(string, *colors)
90
- if Chef::Config[:color]
91
- @out.puts highline.color(string, *colors)
92
- else
93
- @out.puts string
94
- end
95
- end
96
-
97
- end
98
-
99
-
100
60
  # == Formatters::Base
101
61
  # Base class that all formatters should inherit from.
102
62
  class Base < EventDispatch::Base
@@ -112,7 +72,7 @@ class Chef
112
72
  attr_reader :output
113
73
 
114
74
  def initialize(out, err)
115
- @output = Outputter.new(out, err)
75
+ @output = IndentableOutputStream.new(out, err)
116
76
  end
117
77
 
118
78
  def puts(*args)
@@ -123,8 +83,20 @@ class Chef
123
83
  @output.print(*args)
124
84
  end
125
85
 
86
+ def puts_line(*args)
87
+ @output.puts_line(*args)
88
+ end
89
+
90
+ def start_line(*args)
91
+ @output.start_line(*args)
92
+ end
93
+
94
+ def indent_by(amount)
95
+ @output.indent += amount
96
+ end
97
+
126
98
  # Input: a Formatters::ErrorDescription object.
127
- # Outputs error to SDOUT.
99
+ # Outputs error to STDOUT.
128
100
  def display_error(description)
129
101
  puts("")
130
102
  description.display(output)
@@ -237,7 +209,7 @@ class Chef
237
209
 
238
210
 
239
211
  # == NullFormatter
240
- # Formatter that doesn't actually produce any ouput. You can use this to
212
+ # Formatter that doesn't actually produce any output. You can use this to
241
213
  # disable the use of output formatters.
242
214
  class NullFormatter < Base
243
215
 
@@ -247,4 +219,3 @@ class Chef
247
219
 
248
220
  end
249
221
  end
250
-
@@ -10,7 +10,6 @@ class Chef
10
10
 
11
11
  attr_reader :start_time, :end_time
12
12
  cli_name(:doc)
13
-
14
13
 
15
14
  def initialize(out, err)
16
15
  super
@@ -26,7 +25,7 @@ class Chef
26
25
  end
27
26
 
28
27
  def run_start(version)
29
- puts "Starting Chef Client, version #{version}"
28
+ puts_line "Starting Chef Client, version #{version}"
30
29
  end
31
30
 
32
31
  def total_resources
@@ -36,18 +35,18 @@ class Chef
36
35
  def run_completed(node)
37
36
  @end_time = Time.now
38
37
  if Chef::Config[:why_run]
39
- puts "Chef Client finished, #{@updated_resources}/#{total_resources} resources would have been updated"
38
+ puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources would have been updated"
40
39
  else
41
- puts "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{elapsed_time} seconds"
40
+ puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{elapsed_time} seconds"
42
41
  end
43
42
  end
44
43
 
45
44
  def run_failed(exception)
46
45
  @end_time = Time.now
47
46
  if Chef::Config[:why_run]
48
- puts "Chef Client failed. #{@updated_resources} resources would have been updated"
47
+ puts_line "Chef Client failed. #{@updated_resources} resources would have been updated"
49
48
  else
50
- puts "Chef Client failed. #{@updated_resources} resources updated in #{elapsed_time} seconds"
49
+ puts_line "Chef Client failed. #{@updated_resources} resources updated in #{elapsed_time} seconds"
51
50
  end
52
51
  end
53
52
 
@@ -61,7 +60,7 @@ class Chef
61
60
 
62
61
  # About to attempt to register as +node_name+
63
62
  def registration_start(node_name, config)
64
- puts "Creating a new client identity for #{node_name} using the validator key."
63
+ puts_line "Creating a new client identity for #{node_name} using the validator key."
65
64
  end
66
65
 
67
66
  def registration_completed
@@ -82,7 +81,7 @@ class Chef
82
81
 
83
82
  # Called before the cookbook collection is fetched from the server.
84
83
  def cookbook_resolution_start(expanded_run_list)
85
- puts "resolving cookbooks for run list: #{expanded_run_list.inspect}"
84
+ puts_line "resolving cookbooks for run list: #{expanded_run_list.inspect}"
86
85
  end
87
86
 
88
87
  # Called when there is an error getting the cookbook collection from the
@@ -111,12 +110,13 @@ class Chef
111
110
 
112
111
  # Called before cookbook sync starts
113
112
  def cookbook_sync_start(cookbook_count)
114
- puts "Synchronizing Cookbooks:"
113
+ puts_line "Synchronizing Cookbooks:"
114
+ indent
115
115
  end
116
116
 
117
117
  # Called when cookbook +cookbook_name+ has been sync'd
118
118
  def synchronized_cookbook(cookbook_name)
119
- puts " - #{cookbook_name}"
119
+ puts_line "- #{cookbook_name}"
120
120
  end
121
121
 
122
122
  # Called when an individual file in a cookbook has been updated
@@ -125,11 +125,12 @@ class Chef
125
125
 
126
126
  # Called after all cookbooks have been sync'd.
127
127
  def cookbook_sync_complete
128
+ unindent
128
129
  end
129
130
 
130
131
  # Called when cookbook loading starts.
131
132
  def library_load_start(file_count)
132
- puts "Compiling Cookbooks..."
133
+ puts_line "Compiling Cookbooks..."
133
134
  end
134
135
 
135
136
  # Called after a file in a cookbook is loaded.
@@ -142,11 +143,12 @@ class Chef
142
143
 
143
144
  # Called before convergence starts
144
145
  def converge_start(run_context)
145
- puts "Converging #{run_context.resource_collection.all_resources.size} resources"
146
+ puts_line "Converging #{run_context.resource_collection.all_resources.size} resources"
146
147
  end
147
148
 
148
149
  # Called when the converge phase is finished.
149
150
  def converge_complete
151
+ unindent if @current_recipe
150
152
  end
151
153
 
152
154
  # Called before action is executed on a resource.
@@ -157,12 +159,15 @@ class Chef
157
159
  resource_recipe = "<Dynamically Defined Resource>"
158
160
  end
159
161
 
160
- if resource_recipe != @current_recipe
161
- puts "Recipe: #{resource_recipe}"
162
+ if resource_recipe != @current_recipe && !resource.enclosing_provider
163
+ unindent if @current_recipe
164
+ puts_line "Recipe: #{resource_recipe}"
162
165
  @current_recipe = resource_recipe
166
+ indent
163
167
  end
164
168
  # TODO: info about notifies
165
- print " * #{resource} action #{action}"
169
+ start_line "* #{resource} action #{action}", :stream => resource
170
+ indent
166
171
  end
167
172
 
168
173
  # Called when a resource fails, but will retry.
@@ -172,12 +177,14 @@ class Chef
172
177
  # Called when a resource fails and will not be retried.
173
178
  def resource_failed(resource, action, exception)
174
179
  super
180
+ unindent
175
181
  end
176
182
 
177
183
  # Called when a resource action has been skipped b/c of a conditional
178
184
  def resource_skipped(resource, action, conditional)
179
185
  # TODO: more info about conditional
180
- puts " (skipped due to #{conditional.short_description})"
186
+ puts " (skipped due to #{conditional.short_description})", :stream => resource
187
+ unindent
181
188
  end
182
189
 
183
190
  # Called after #load_current_resource has run.
@@ -187,11 +194,13 @@ class Chef
187
194
  # Called when a resource has no converge actions, e.g., it was already correct.
188
195
  def resource_up_to_date(resource, action)
189
196
  @up_to_date_resources+= 1
190
- puts " (up to date)"
197
+ puts " (up to date)", :stream => resource
198
+ unindent
191
199
  end
192
200
 
193
201
  def resource_bypassed(resource, action, provider)
194
- puts " (Skipped: whyrun not supported by provider #{provider.class.name})"
202
+ puts " (Skipped: whyrun not supported by provider #{provider.class.name})", :stream => resource
203
+ unindent
195
204
  end
196
205
 
197
206
  def output_record(line)
@@ -207,12 +216,12 @@ class Chef
207
216
  next if line.nil?
208
217
  output_record line
209
218
  if line.kind_of? String
210
- @output.color "\n - #{prefix}#{line}", :green
219
+ start_line "- #{prefix}#{line}", :green
211
220
  elsif line.kind_of? Array
212
221
  # Expanded output - delta
213
222
  # @todo should we have a resource_update_delta callback?
214
223
  line.each do |detail|
215
- @output.color "\n #{detail}", :white
224
+ start_line detail, :white
216
225
  end
217
226
  end
218
227
  end
@@ -221,28 +230,36 @@ class Chef
221
230
  # Called after a resource has been completely converged.
222
231
  def resource_updated(resource, action)
223
232
  @updated_resources += 1
233
+ unindent
224
234
  puts "\n"
225
235
  end
226
236
 
227
237
  # Called when resource current state load is skipped due to the provider
228
238
  # not supporting whyrun mode.
229
239
  def resource_current_state_load_bypassed(resource, action, current_resource)
230
- @output.color("\n * Whyrun not supported for #{resource}, bypassing load.", :yellow)
240
+ puts_line("* Whyrun not supported for #{resource}, bypassing load.", :yellow)
241
+ end
242
+
243
+ def stream_output(stream, output, options = {})
244
+ print(output, { :stream => stream }.merge(options))
231
245
  end
232
246
 
233
247
  # Called before handlers run
234
248
  def handlers_start(handler_count)
235
- puts "\nRunning handlers:"
249
+ puts ''
250
+ puts "Running handlers:"
251
+ indent
236
252
  end
237
253
 
238
254
  # Called after an individual handler has run
239
255
  def handler_executed(handler)
240
- puts " - #{handler.class.name}"
256
+ puts_line "- #{handler.class.name}"
241
257
  end
242
258
 
243
259
  # Called after all handlers have executed
244
260
  def handlers_completed
245
- puts "Running handlers complete\n"
261
+ unindent
262
+ puts_line "Running handlers complete\n"
246
263
  end
247
264
 
248
265
  # Called when a provider makes an assumption after a failed assertion
@@ -250,7 +267,7 @@ class Chef
250
267
  def whyrun_assumption(action, resource, message)
251
268
  return unless message
252
269
  [ message ].flatten.each do |line|
253
- @output.color("\n * #{line}", :yellow)
270
+ start_line("* #{line}", :yellow)
254
271
  end
255
272
  end
256
273
 
@@ -259,9 +276,17 @@ class Chef
259
276
  return unless message
260
277
  color = Chef::Config[:why_run] ? :yellow : :red
261
278
  [ message ].flatten.each do |line|
262
- @output.color("\n * #{line}", color)
279
+ start_line("* #{line}", color)
263
280
  end
264
281
  end
282
+
283
+ def indent
284
+ indent_by(2)
285
+ end
286
+
287
+ def unindent
288
+ indent_by(-2)
289
+ end
265
290
  end
266
291
  end
267
292
  end
@@ -0,0 +1,165 @@
1
+ class Chef
2
+ module Formatters
3
+ # Handles basic indentation and colorization tasks
4
+ class IndentableOutputStream
5
+
6
+ attr_reader :out
7
+ attr_reader :err
8
+ attr_accessor :indent
9
+ attr_reader :line_started
10
+ attr_accessor :current_stream
11
+ attr_reader :semaphore
12
+
13
+ def initialize(out, err)
14
+ @out, @err = out, err
15
+ @indent = 0
16
+ @line_started = false
17
+ @semaphore = Mutex.new
18
+ end
19
+
20
+ def highline
21
+ @highline ||= begin
22
+ require 'highline'
23
+ HighLine.new
24
+ end
25
+ end
26
+
27
+ # Print text. This will start a new line and indent if necessary
28
+ # but will not terminate the line (future print and puts statements
29
+ # will start off where this print left off).
30
+ def color(string, *args)
31
+ print(string, from_args(args))
32
+ end
33
+
34
+ # Print the start of a new line. This will terminate any existing lines and
35
+ # cause indentation but will not move to the next line yet (future 'print'
36
+ # and 'puts' statements will stay on this line).
37
+ def start_line(string, *args)
38
+ print(string, from_args(args, :start_line => true))
39
+ end
40
+
41
+ # Print a line. This will continue from the last start_line or print,
42
+ # or start a new line and indent if necessary.
43
+ def puts(string, *args)
44
+ print(string, from_args(args, :end_line => true))
45
+ end
46
+
47
+ # Print an entire line from start to end. This will terminate any existing
48
+ # lines and cause indentation.
49
+ def puts_line(string, *args)
50
+ print(string, from_args(args, :start_line => true, :end_line => true))
51
+ end
52
+
53
+ # Print a string.
54
+ #
55
+ # == Arguments
56
+ # string: string to print.
57
+ # options: a hash with these possible options:
58
+ # - :stream => OBJ: unique identifier for a stream. If two prints have
59
+ # different streams, they will print on separate lines.
60
+ # Otherwise, they will stay together.
61
+ # - :start_line => BOOLEAN: if true, print will begin on a blank (indented) line.
62
+ # - :end_line => BOOLEAN: if true, current line will be ended.
63
+ # - :name => STRING: a name to prefix in front of a stream. It will be printed
64
+ # once (with the first line of the stream) and subsequent lines
65
+ # will be indented to match.
66
+ #
67
+ # == Alternative
68
+ #
69
+ # You may also call print('string', :red) (a list of colors a la Highline.color)
70
+ def print(string, *args)
71
+ options = from_args(args)
72
+
73
+ # Make sure each line stays a unit even with threads sending output
74
+ semaphore.synchronize do
75
+ if should_start_line?(options)
76
+ move_to_next_line
77
+ end
78
+
79
+ print_string(string, options)
80
+
81
+ if should_end_line?(options)
82
+ move_to_next_line
83
+ end
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def should_start_line?(options)
90
+ options[:start_line] || @current_stream != options[:stream]
91
+ end
92
+
93
+ def should_end_line?(options)
94
+ options[:end_line] && @line_started
95
+ end
96
+
97
+ def from_args(colors, merge_options = {})
98
+ if colors.size == 1 && colors[0].kind_of?(Hash)
99
+ merge_options.merge(colors[0])
100
+ else
101
+ merge_options.merge({ :colors => colors })
102
+ end
103
+ end
104
+
105
+ def print_string(string, options)
106
+ if string.empty?
107
+ if options[:end_line]
108
+ print_line('', options)
109
+ end
110
+ else
111
+ string.lines.each do |line|
112
+ print_line(line, options)
113
+ end
114
+ end
115
+ end
116
+
117
+ def print_line(line, options)
118
+ indent_line(options)
119
+
120
+ # Note that the next line will need to be started
121
+ if line[-1..-1] == "\n"
122
+ @line_started = false
123
+ end
124
+
125
+ if Chef::Config[:color] && options[:colors]
126
+ @out.print highline.color(line, *options[:colors])
127
+ else
128
+ @out.print line
129
+ end
130
+ end
131
+
132
+ def move_to_next_line
133
+ if @line_started
134
+ @out.puts ''
135
+ @line_started = false
136
+ end
137
+ end
138
+
139
+ def indent_line(options)
140
+ if !@line_started
141
+
142
+ # Print indents. If there is a stream name, either print it (if we're
143
+ # switching streams) or print enough blanks to match
144
+ # the indents.
145
+ if options[:name]
146
+ if @current_stream != options[:stream]
147
+ @out.print "#{(' ' * indent)}[#{options[:name]}] "
148
+ else
149
+ @out.print ' ' * (indent + 3 + options[:name].size)
150
+ end
151
+ else
152
+ # Otherwise, just print indents.
153
+ @out.print ' ' * indent
154
+ end
155
+
156
+ if @current_stream != options[:stream]
157
+ @current_stream = options[:stream]
158
+ end
159
+
160
+ @line_started = true
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end