diff options
48 files changed, 1121 insertions, 385 deletions
diff --git a/.travis.yml b/.travis.yml index 5504f4aa21..e93d6ce246 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,9 +35,12 @@ matrix: - rvm: 2.2 sudo: true bundler_args: --without server docgen maintenance + - rvm: 2.3.0 + sudo: true + bundler_args: --without server docgen maintenance - rvm: rbx sudo: true - bundler_args: --without server docgen maintenance ruby_prof + bundler_args: --without server docgen maintenance ruby_prof pry - rvm: 2.2 env: "GEMFILE_MOD=\"gem 'chef-zero', github: 'chef/chef-zero'\"" script: bundle exec rake chef_zero_spec @@ -146,10 +149,11 @@ matrix: - sudo cat /var/log/squid3/access.log allow_failures: - - rvm: rbx - rvm: 2.2 - env: "GEMFILE_MOD=\"gem 'halite', github: 'poise/halite'\"" - script: bundle exec rake halite_spec + env: "GEMFILE_MOD=\"gem 'poise', github: 'poise/poise'\"" + script: bundle exec rake poise_spec + - rvm: 2.3.0 + - rvm: rbx notifications: on_change: true diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md index 79d60b856b..90b57c7e34 100644 --- a/DOC_CHANGES.md +++ b/DOC_CHANGES.md @@ -6,128 +6,17 @@ Example Doc Change: Description of the required change. --> -### `chef_version` and `ohai_version` +## Doc changes for Chef 12.9 -see: https://docs.chef.io/release/12-6/release_notes.html#new-metadata-rb-settings +### New timeout option added to `knife ssh` -The metadata.rb DSL is extended to support `chef_version` and `ohai_version` to establish ranges -of chef and ohai versions that the cookbook supports. +When doing a `knife ssh` call, if a connection to a host is not able +to succeed due to host unreachable or down, the entire call can hang. In +order to prevent this from happening, a new timeout option has been added +to allow a connection timeout to be passed to the underlying SSH call +(see ConnectTimeout setting in http://linux.die.net/man/5/ssh_config) -When the running chef or ohai version does not match, then the chef-client run will abort with an -exception immediately after cookbooks have been synchronized before any cookbook contents are -parsed. - -The format of the dependencies is based on rubygems (and implemented with rubygems code). Pessimistic -version constraints, floor and ceiling constraints, and specifying multiple constraints are all valid. - -Examples: - -``` -# matches any 12.x version, but not 11.x or 13.x -chef_version "~> 12" -``` - -``` -# matches any 12.x, 13.x, etc version -chef_version ">= 12" -``` - -``` -# matches any chef 12 version >= 12.5.1 or any chef 13 version -chef_version ">= 12.5.1", "< 14.0" -``` - -``` -# matches chef 11 >= 11.18.4 or chef 12 >= 12.5.1 (i.e. depends on a backported bugfix) -chef_version ">= 11.18.12", "< 12.0" -chef_version ">= 12.5.1", "< 13.0" -``` - -As seen in the last example multiple constraints are OR'd. - -There is currently no support in supermarket for making this metadata visible in /universe to -depsolvers, or support in Berksfile/PolicyFile for automatically pruning cookbooks that fail -to match. - -### `ksh` resources - -Use the ksh resource to execute scripts using the Korn shell (ksh) interpreter. -This resource may also use any of the actions and properties that are available -to the execute resource. - -Example: -```ruby -ksh 'hello world' do - code <<-EOH - echo "Hello world!" - echo "Current directory: " $cwd - EOH -end -``` - -See https://docs.chef.io/release/12-6/resource_ksh.html for more info. - -### `dsc_resource` resource - -Added reboot_action attribute to dsc_resource. - -If the DSC resource indicates that it requires a reboot, reboot_action can use the reboot resource to -either reboot immediately (:reboot_now) or queue a reboot (:request_reboot). The default value of reboot_action is :nothing. - -The following two items in the dsc_resource doc in Chef 12.6.0 are ONLY applicable for PowerShell versions earlier than 5.0.10586.0. The latest version of WMF 5 has relaxed the limitation that prevented us from running in non-disabled RefreshMode configuration, and including both dsc_script and dsc_resource in the same run list: -1. The RefreshMode configuration setting in the Local Configuration Manager must be set to Disabled -2. The dsc_script resource may not be used in the same run-list with the dsc_resource. This is because the dsc_script resource requires that RefreshMode in the Local Configuration Manager be set to Push, whereas the dsc_resource resource requires it to be set to Disabled - - -### `knife bootstrap --ssh-identity-file` - -The --identity-file option to `knife bootstrap` has been deprecated in favor of `knife bootstrap --ssh-identity-file` -to better align with other ssh related options. - -### `windows_package` resource - -`windows_package` now supports more than just `MSI`. Most common windows installer types are supported including Inno Setup, Nullsoft, Wise and InstallShield. The new allowed `installer_type` values are: `inno`, `nsis`, `wise`, `installshield`, `custom`, and `msi`. - -**Non `:msi` package installation** -When installing non `:msi` packages, the `package_name` should match the display name used for the installed package in the "Add/Remove Programs" settings application. This value can also be found in the following registry locations: -* HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall -* HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall -* HKEY_LOCAL_MACHINE\Software\Wow6464Node\Microsoft\Windows\CurrentVersion\Uninstall - - Further, non `:msi` packages must explicitly include a package `source` attribute when using the `:install` action. Unlike `:msi` packages, they will not default to the package name if missing. Without the name matching the software's display name, non `:msi` packages will always reconverge on `:install`. - -Also, while being able to download remote installers from a `HTTP` resource is not new, it looks as though the top of the docs page is incorrect stating that only local installers can be used as a source. - -Example Nullsoft (`nsis`) package resource: -``` -package 'Mercurial 3.6.1 (64-bit)' do - source 'http://mercurial.selenic.com/release/windows/Mercurial-3.6.1-x64.exe' - checksum 'febd29578cb6736163d232708b834a2ddd119aa40abc536b2c313fc5e1b5831d' -end -``` - -Example Custom `windows_package` resource: -``` -package 'Microsoft Visual C++ 2005 Redistributable' do - source 'https://download.microsoft.com/download/6/B/B/6BB661D6-A8AE-4819-B79F-236472F6070C/vcredist_x86.exe' - installer_type :custom - options '/Q' -end -``` -Using a `:custom` package is one way to install a non `.msi` file that embeds an `msi` based installer. - -**`windows_package` removal** -Packages can now be removed without the need to include the package `source`. The relevent uninstall metadata will now be discovered from the registry. -``` -package 'Mercurial 3.6.1 (64-bit)' do - action :remove -end -``` -For non `:msi` packages, it is important that the package name used is EXACTLY the same as the display name found in "Add/Remove programs" or the `DisplayName` property in the appropriate registry key: -* HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall -* HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall -* HKEY_LOCAL_MACHINE\Software\Wow6464Node\Microsoft\Windows\CurrentVersion\Uninstall - -When removing `:msi` packages, this same package naming rule applies if the `source` is omitted. - -Note that if there are multiple versions of a package installed with the same display name, all packages will be removed unless a version is provided in the `version` attribute or can be discovered in the `source` installer file. +The timeout setting can be passed in via a command line parameter +(`-t` or `--ssh-timeout`) or via a knife config +(`Chef::Config[:knife][:ssh_timeout]`). The value of the timeout is set +in seconds. diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 244f2ed1ad..8f47b40aef 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -44,6 +44,19 @@ To mention the team, use @chef/client-core * [Ranjib Dey](https://github.com/ranjib) * [Matt Wrock](https://github.com/mwrock) +## Ohai + +To mention the team, use @chef/ohai + +### Lieutenant + +* [Claire McQuin](https://github.com/mcquin) + +### Maintainers + +* [Bryan McLellan](https://github.com/btm) +* [Tim Smith](https://github.com/tas50) + ## Dev Tools ChefDK, Chef Zero, Knife, Chef Apply and Chef Shell. @@ -71,6 +84,22 @@ To mention the team, use @chef/client-test-tools * [Lamont Granquist](https://github.com/lamont-granquist) * [Ranjib Dey](https://github.com/ranjib) +## Chef Provisioning + +Chef Provisioning and Drivers. Supported Drivers are listed in the [README](https://github.com/chef/chef-provisioning/blob/master/README.md#chef-provisioning). + +To mention the team, use @chef/provisioning + +### Lieutenant + +* [Tyler Ball](https://github.com/tyler-ball) + +### Maintainers + +* [John Keiser](https://github.com/jkeiser) +* [Stuart Preston](https://github.com/stuartpreston) +* [JJ Asghar](https://github.com/jjasghar) + ## Platform Specific Components The specific components of Chef related to a given platform - including (but not limited to) resources, providers, and the core DSL. @@ -161,6 +190,30 @@ To mention the team, use @chef/client-debian * [Lamont Granquist](https://github.com/lamont-granquist) +## Cisco NX-OS + +To mention the team, use @chef/client-nxos + +### Lieutenant + +* [Carl Perry](https://github.com/edolnx) + +### Maintainers + +* [Carl Perry](https://github.com/edolnx) + +## Cisco IOS XR + +To mention the team, use @chef/client-iosxr + +### Lieutenant + +* [Carl Perry](https://github.com/edolnx) + +### Maintainers + +* [Carl Perry](https://github.com/edolnx) + ## Fedora To mention the team, use @chef/client-fedora diff --git a/MAINTAINERS.toml b/MAINTAINERS.toml index 3b9507938c..a75a2f532e 100644 --- a/MAINTAINERS.toml +++ b/MAINTAINERS.toml @@ -51,6 +51,17 @@ another component. "mwrock" ] + [Org.Components.Ohai] + title = "Ohai" + team = "ohai" + + lieutenant = "mcquin" + + maintainers = [ + "btm", + "tas50" + ] + [Org.Components.DevTools] title = "Dev Tools" team = "client-dev-tools" @@ -84,6 +95,21 @@ another component. "ranjib" ] + [Org.Components.Provisioning] + title = "Chef Provisioning" + team = "provisioning" + text = """ +Chef Provisioning and Drivers. Supported Drivers are listed in the [README](https://github.com/chef/chef-provisioning/blob/master/README.md#chef-provisioning). +""" + + lieutenant = "tyler-ball" + + maintainers = [ + "jkeiser", + "stuartpreston", + "jjasghar" + ] + [Org.Components.Subsystems] title = "Platform Specific Components" text = """ @@ -360,3 +386,21 @@ The specific components of Chef related to a given platform - including (but not [people.cperry] Name = "Carl Perry" GitHub = "edolnx" + + [people.tas50] + Name = "Tim Smith" + GitHub = "tas50" + + [people.jkeiser] + Name = "John Keiser" + GitHub = "jkeiser" + + [people.stuartpreston] + Name = "Stuart Preston" + GitHub = "stuartpreston" + + [people.jjasghar] + Name = "JJ Asghar" + GitHub = "jjasghar" + Twitter = "jjasghar" + IRC = "j^2" diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6f67aa7b09..c25d6396c2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -36,3 +36,20 @@ from Chef::Provider when mixing `use_inline_resources` into classes that only in class. If any code has been written like this, it should be modified to correctly inherit from Chef::Provider::LWRPBase instead (which will have the side effect of fixing it so that it correctly works on Chef 11.0-12.5 as well). + +## Shorthand options for `log_location` + +The `log_location` setting now accepts shorthand `:syslog` and +`:win_evt` options. `:syslog` is shorthand for `Chef::Log::Syslog.new` +and `:win_evt` is shorthand for `Chef::Log::WinEvt.new`. All previously +valid options are still valid, including Logger or Logger-like +instances, e.g. `Chef::Log::Syslog.new` with other args than the +defaults. + +## chef-client `--daemonize` option now takes an optional integer argument + +Optional integer argument (.e.g `chef-client --daemonize 5`) is the +number of seconds to wait before the first daemonized run. See +[#3305] for background. + +[#3305]: https://github.com/chef/chef/issues/3305 @@ -20,6 +20,7 @@ VERSION = IO.read(File.expand_path("../VERSION", __FILE__)).strip require "rubygems" +require "chef/version" require "chef-config/package_task" require "rdoc/task" require_relative "tasks/rspec" @@ -1 +1 @@ -12.8.6
\ No newline at end of file +12.9.29
\ No newline at end of file diff --git a/acceptance/Gemfile b/acceptance/Gemfile index 4573bc5fb3..72dded468c 100644 --- a/acceptance/Gemfile +++ b/acceptance/Gemfile @@ -1,8 +1,7 @@ source "https://rubygems.org" -gem "mixlib-install", github: "chef/mixlib-install" gem "chef-acceptance", github: "chef/chef-acceptance" -gem "test-kitchen", github: "sersut/test-kitchen", branch: "sersut/mixlib-install-update" +gem "test-kitchen" gem "kitchen-ec2", github: "test-kitchen/kitchen-ec2", branch: "jk/image-search-only" gem "kitchen-inspec" gem "inspec" @@ -10,5 +9,5 @@ gem "inspec" # puts in a box_url for bento when a vagrant box in atlas is specified gem "kitchen-vagrant", github: "test-kitchen/kitchen-vagrant" gem "windows_chef_zero" -gem "winrm-transport" +gem "winrm-fs" gem "berkshelf" diff --git a/acceptance/omnitruck/.acceptance/acceptance-cookbook/.gitignore b/acceptance/omnitruck/.acceptance/acceptance-cookbook/.gitignore new file mode 100644 index 0000000000..041413b040 --- /dev/null +++ b/acceptance/omnitruck/.acceptance/acceptance-cookbook/.gitignore @@ -0,0 +1,2 @@ +nodes/ +tmp/ diff --git a/acceptance/omnitruck/.acceptance/acceptance-cookbook/metadata.rb b/acceptance/omnitruck/.acceptance/acceptance-cookbook/metadata.rb new file mode 100644 index 0000000000..4c7c42d9bd --- /dev/null +++ b/acceptance/omnitruck/.acceptance/acceptance-cookbook/metadata.rb @@ -0,0 +1 @@ +name 'acceptance-cookbook' diff --git a/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/destroy.rb new file mode 100644 index 0000000000..f890b597fe --- /dev/null +++ b/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/destroy.rb @@ -0,0 +1 @@ +log "NOOP 'destroy' recipe from the acceptance-cookbook in directory '#{node['chef-acceptance']['suite-dir']}'" diff --git a/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/provision.rb new file mode 100644 index 0000000000..64ef7581ac --- /dev/null +++ b/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/provision.rb @@ -0,0 +1 @@ +log "NOOP 'provision' recipe from the acceptance-cookbook in directory '#{node['chef-acceptance']['suite-dir']}'" diff --git a/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/verify.rb new file mode 100644 index 0000000000..7db51450e1 --- /dev/null +++ b/acceptance/omnitruck/.acceptance/acceptance-cookbook/recipes/verify.rb @@ -0,0 +1,61 @@ +control_group "omnitruck" do + require 'chef/http' + require 'chef/json_compat' + + # We do this to be able to reference 'rest' both inside and outside example + # blocks + rest = Chef::HTTP.new("https://omnitruck.chef.io/chef/metadata", headers: {"Accept" => "application/json"}) + let(:rest) { rest } + + def request(url) + Chef::JSONCompat.parse(rest.get(url))["sha256"] + end + + shared_examples "32 matches 64" do |version| + it "only returns 32-bit packages" do + sha32 = request("?p=windows&pv=2012r2&v=#{version}&m=i386") + sha64 = request("?p=windows&pv=2012r2&v=#{version}&m=x86_64") + expect(sha32).to eq(sha64) + end + end + + context "from the current channel" do + it "returns both 32-bit and 64-bit packages" do + # We cannot verify from the returned URL if the package is 64 or 32 bit because + # it is often lying, so we just make sure they are different. + # The current channel is often cleaned so only the latest builds are in + # it, so we just request the latest version instead of trying to check + # old versions + sha32 = request("?p=windows&pv=2012r2&m=i386&prerelease=true") + sha64 = request("?p=windows&pv=2012r2&m=x86_64&prerelease=true") + expect(sha32).to_not eq(sha64) + end + end + + context "from the stable channel" do + %w{11 12.3 12.4.2 12.6.0 12.8.1}.each do |version| + describe "with version #{version}" do + include_examples "32 matches 64", version + end + end + + begin + rest.get("?p=windows&pv=2012r2&v=12.9") + describe "with version 12.9" do + it "returns both 32-bit and 64-bit packages" do + sha32 = request("?p=windows&pv=2012r2&v=12.9&m=i386") + sha64 = request("?p=windows&pv=2012r2&v=12.9&m=x86_64") + expect(sha32).to_not eq(sha64) + end + end + rescue Net::HTTPServerException => e + # Once 12.9 is released this will stop 404ing and the example + # will be executed + unless e.response.code == "404" + raise + end + end + + end + +end diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index a00d9e8e05..2118595ea0 100644 --- a/chef-config/lib/chef-config/version.rb +++ b/chef-config/lib/chef-config/version.rb @@ -21,7 +21,7 @@ module ChefConfig CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.8.6" + VERSION = "12.9.29" end # diff --git a/chef.gemspec b/chef.gemspec index 8d19051dbd..e1055e01b5 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -13,7 +13,7 @@ Gem::Specification.new do |s| s.email = "adam@chef.io" s.homepage = "http://www.chef.io" - s.required_ruby_version = ">= 2.0.0" + s.required_ruby_version = ">= 2.1.0" s.add_dependency "chef-config", "= #{Chef::VERSION}" diff --git a/lib/chef/application.rb b/lib/chef/application.rb index cd3e7f8c24..7dbffd8dec 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -141,6 +141,7 @@ class Chef # that a user has configured a log_location in client.rb, but is running # chef-client by hand to troubleshoot a problem. def configure_logging + configure_log_location Chef::Log.init(MonoLogger.new(Chef::Config[:log_location])) if want_additional_logger? configure_stdout_logger @@ -151,6 +152,20 @@ class Chef Chef::Application.fatal!("Aborting due to invalid 'log_location' configuration", 2) end + # Turn `log_location :syslog` and `log_location :win_evt` into the + # appropriate loggers. + def configure_log_location + log_location = Chef::Config[:log_location] + return unless log_location.respond_to?(:to_sym) + + Chef::Config[:log_location] = + case log_location.to_sym + when :syslog then Chef::Log::Syslog.new + when :win_evt then Chef::Log::WinEvt.new + else log_location # Probably a path; let MonoLogger sort it out + end + end + def configure_stdout_logger stdout_logger = MonoLogger.new(STDOUT) stdout_logger.formatter = Chef::Log.logger.formatter diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index 500aa8ac59..ac46e533dd 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -105,10 +105,12 @@ class Chef::Application::Client < Chef::Application unless Chef::Platform.windows? option :daemonize, - :short => "-d", - :long => "--daemonize", - :description => "Daemonize the process", - :proc => lambda { |p| true } + :short => "-d [WAIT]", + :long => "--daemonize [WAIT]", + :description => + "Daemonize the process. Accepts an optional integer which is the " \ + "number of seconds to wait before the first daemonized run.", + :proc => lambda { |wait| wait =~ /^\d+$/ ? wait.to_i : true } end option :pid_file, @@ -430,33 +432,38 @@ class Chef::Application::Client < Chef::Application def interval_run_chef_client if Chef::Config[:daemonize] Chef::Daemon.daemonize("chef-client") + + # Start first daemonized run after configured number of seconds + if Chef::Config[:daemonize].is_a?(Integer) + sleep_then_run_chef_client(Chef::Config[:daemonize]) + end end loop do - begin - @signal = test_signal - if @signal != IMMEDIATE_RUN_SIGNAL - sleep_sec = time_to_sleep - Chef::Log.debug("Sleeping for #{sleep_sec} seconds") - interval_sleep(sleep_sec) - end - - @signal = nil - run_chef_client(Chef::Config[:specific_recipes]) + sleep_then_run_chef_client(time_to_sleep) + Chef::Application.exit!("Exiting", 0) if !Chef::Config[:interval] + end + end - Chef::Application.exit!("Exiting", 0) if !Chef::Config[:interval] - rescue SystemExit => e - raise - rescue Exception => e - if Chef::Config[:interval] - Chef::Log.error("#{e.class}: #{e}") - Chef::Log.debug("#{e.class}: #{e}\n#{e.backtrace.join("\n")}") - retry - else - Chef::Application.fatal!("#{e.class}: #{e.message}", 1) - end - end + def sleep_then_run_chef_client(sleep_sec) + @signal = test_signal + unless @signal == IMMEDIATE_RUN_SIGNAL + Chef::Log.debug("Sleeping for #{sleep_sec} seconds") + interval_sleep(sleep_sec) + end + @signal = nil + + run_chef_client(Chef::Config[:specific_recipes]) + rescue SystemExit => e + raise + rescue Exception => e + if Chef::Config[:interval] + Chef::Log.error("#{e.class}: #{e}") + Chef::Log.debug("#{e.class}: #{e}\n#{e.backtrace.join("\n")}") + retry end + + Chef::Application.fatal!("#{e.class}: #{e.message}", 1) end def test_signal diff --git a/lib/chef/knife/cookbook_create.rb b/lib/chef/knife/cookbook_create.rb index 1e19535fbf..950de380f8 100644 --- a/lib/chef/knife/cookbook_create.rb +++ b/lib/chef/knife/cookbook_create.rb @@ -185,13 +185,11 @@ EOH unless File.exists?(File.join(dir, cookbook_name, "CHANGELOG.md")) open(File.join(dir, cookbook_name, "CHANGELOG.md"), "w") do |file| file.puts <<-EOH -#{cookbook_name} CHANGELOG -#{'=' * "#{cookbook_name} CHANGELOG".length} +# #{cookbook_name} CHANGELOG This file is used to list changes made in each version of the #{cookbook_name} cookbook. -0.1.0 ------ +## 0.1.0 - [your_name] - Initial release of #{cookbook_name} - - - @@ -205,7 +203,7 @@ EOH def create_readme(dir, cookbook_name, readme_format) msg("** Creating README for cookbook: #{cookbook_name}") - unless File.exists?(File.join(dir, cookbook_name, "README.#{readme_format}")) + unless File.exist?(File.join(dir, cookbook_name, "README.#{readme_format}")) open(File.join(dir, cookbook_name, "README.#{readme_format}"), "w") do |file| case readme_format when "rdoc" @@ -273,27 +271,37 @@ Authors: TODO: List authors EOH when "md", "mkd", "txt" file.puts <<-EOH -#{cookbook_name} Cookbook -#{'=' * "#{cookbook_name} Cookbook".length} +# #{cookbook_name} Cookbook + TODO: Enter the cookbook description here. e.g. This cookbook makes your favorite breakfast sandwich. -Requirements ------------- +## Requirements + TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc. e.g. -#### packages +### Platforms + +- SandwichOS + +### Chef + +- Chef 12.0 or later + +### Cookbooks + - `toaster` - #{cookbook_name} needs toaster to brown your bagel. -Attributes ----------- +## Attributes + TODO: List your cookbook attributes here. e.g. -#### #{cookbook_name}::default +### #{cookbook_name}::default + <table> <tr> <th>Key</th> @@ -309,9 +317,10 @@ e.g. </tr> </table> -Usage ------ -#### #{cookbook_name}::default +## Usage + +### #{cookbook_name}::default + TODO: Write usage instructions for each cookbook. e.g. @@ -326,8 +335,8 @@ Just include `#{cookbook_name}` in your node's `run_list`: } ``` -Contributing ------------- +## Contributing + TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section. e.g. @@ -338,9 +347,10 @@ e.g. 5. Run the tests, ensuring they all pass 6. Submit a Pull Request using Github -License and Authors -------------------- +## License and Authors + Authors: TODO: List authors + EOH else file.puts <<-EOH @@ -415,9 +425,9 @@ EOH "All rights reserved" end - unless File.exists?(File.join(dir, cookbook_name, "metadata.rb")) + unless File.exist?(File.join(dir, cookbook_name, "metadata.rb")) open(File.join(dir, cookbook_name, "metadata.rb"), "w") do |file| - if File.exists?(File.join(dir, cookbook_name, "README.#{readme_format}")) + if File.exist?(File.join(dir, cookbook_name, "README.#{readme_format}")) long_description = "long_description IO.read(File.join(File.dirname(__FILE__), 'README.#{readme_format}'))" end file.puts <<-EOH diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb index 4ccc300036..31356b6f48 100644 --- a/lib/chef/knife/ssh.rb +++ b/lib/chef/knife/ssh.rb @@ -80,6 +80,12 @@ class Chef :description => "The ssh port", :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key.strip } + option :ssh_timeout, + :short => "-t SECONDS", + :long => "--ssh-timeout SECONDS", + :description => "The ssh connection timeout", + :proc => Proc.new { |key| Chef::Config[:knife][:ssh_timeout] = key.strip.to_i } + option :ssh_gateway, :short => "-G GATEWAY", :long => "--ssh-gateway GATEWAY", @@ -258,6 +264,9 @@ class Chef # Handle port overrides for the main connection. session_opts[:port] = Chef::Config[:knife][:ssh_port] if Chef::Config[:knife][:ssh_port] session_opts[:port] = config[:ssh_port] if config[:ssh_port] + # Handle connection timeout + session_opts[:timeout] = Chef::Config[:knife][:ssh_timeout] if Chef::Config[:knife][:ssh_timeout] + session_opts[:timeout] = config[:ssh_timeout] if config[:ssh_timeout] # Create the hostspec. hostspec = session_opts[:user] ? "#{session_opts.delete(:user)}@#{host}" : host # Connect a new session on the multi. diff --git a/lib/chef/mixin/which.rb b/lib/chef/mixin/which.rb index de446d93cf..63c84883d5 100644 --- a/lib/chef/mixin/which.rb +++ b/lib/chef/mixin/which.rb @@ -1,5 +1,5 @@ #-- -# Author:: Lamont Granquist <lamont@getchef.io> +# Author:: Lamont Granquist <lamont@chef.io> # Copyright:: Copyright 2010-2016, Chef Software Inc. # License:: Apache License, Version 2.0 # diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb index 3e90797802..40474242f0 100644 --- a/lib/chef/platform/provider_mapping.rb +++ b/lib/chef/platform/provider_mapping.rb @@ -200,10 +200,10 @@ class Chef class_name = resource_type.class.name ? resource_type.class.name.split("::").last : convert_to_class_name(resource_type.resource_name.to_s) - if Chef::Provider.const_defined?(class_name) + if Chef::Provider.const_defined?(class_name, false) Chef::Log.warn("Class Chef::Provider::#{class_name} does not declare 'provides #{convert_to_snake_case(class_name).to_sym.inspect}'.") Chef::Log.warn("This will no longer work in Chef 13: you must use 'provides' to use the resource's DSL.") - return Chef::Provider.const_get(class_name) + return Chef::Provider.const_get(class_name, false) end end nil diff --git a/lib/chef/provider/mount.rb b/lib/chef/provider/mount.rb index cc4a548ac1..9e9ee29bde 100644 --- a/lib/chef/provider/mount.rb +++ b/lib/chef/provider/mount.rb @@ -42,17 +42,13 @@ class Chef end def action_mount - if current_resource.mounted - if mount_options_unchanged? - Chef::Log.debug("#{new_resource} is already mounted") - else - action_remount - end - else + unless current_resource.mounted converge_by("mount #{current_resource.device} to #{current_resource.mount_point}") do mount_fs Chef::Log.info("#{new_resource} mounted") end + else + Chef::Log.debug("#{new_resource} is already mounted") end end diff --git a/lib/chef/provider/noop.rb b/lib/chef/provider/noop.rb new file mode 100644 index 0000000000..207bf7dedb --- /dev/null +++ b/lib/chef/provider/noop.rb @@ -0,0 +1,37 @@ +# +# Author:: Thom May (<thom@chef.io>) +# Copyright:: Copyright (c) 2016 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef + class Provider + class Noop < Chef::Provider + def load_current_resource; end + + def respond_to_missing?(method_sym, include_private = false) + method_sym.to_s.start_with?("action_") || super + end + + def method_missing(method_sym, *arguments, &block) + if method_sym.to_s =~ /^action_/ + Chef::Log.debug("NoOp-ing for #{method_sym}") + else + super + end + end + end + end +end diff --git a/lib/chef/provider/package/homebrew.rb b/lib/chef/provider/package/homebrew.rb index 853a354b28..a105f6d7d0 100644 --- a/lib/chef/provider/package/homebrew.rb +++ b/lib/chef/provider/package/homebrew.rb @@ -127,7 +127,7 @@ class Chef Chef::Log.debug "Executing '#{command}' as user '#{homebrew_user.name}'" # FIXME: this 1800 second default timeout should be deprecated - output = shell_out_with_timeout!(command, :timeout => 1800, :user => homebrew_uid, :environment => { "HOME" => homebrew_user.dir, "RUBYOPT" => nil }) + output = shell_out_with_timeout!(command, :timeout => 1800, :user => homebrew_uid, :environment => { "HOME" => homebrew_user.dir, "RUBYOPT" => nil, "TMPDIR" => nil }) output.stdout.chomp end diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb index 08f8e108d5..200a2d3400 100644 --- a/lib/chef/provider/service/redhat.rb +++ b/lib/chef/provider/service/redhat.rb @@ -57,15 +57,21 @@ class Chef requirements.assert(:all_actions) do |a| chkconfig_file = "/sbin/chkconfig" a.assertion { ::File.exists? chkconfig_file } - a.failure_message Chef::Exceptions::Service, "#{chkconfig_file} dbleoes not exist!" + a.failure_message Chef::Exceptions::Service, "#{chkconfig_file} does not exist!" end - requirements.assert(:start, :enable, :reload, :restart) do |a| + requirements.assert(:enable) do |a| + a.assertion { !@service_missing } + a.failure_message Chef::Exceptions::Service, "#{new_resource}: Service is not known to chkconfig." + a.whyrun "Assuming service would be enabled. The init script is not presently installed." + end + + requirements.assert(:start, :reload, :restart) do |a| a.assertion do - custom_command_for_action?(action) || !@service_missing + new_resource.init_command || custom_command_for_action?(action) || !@service_missing end a.failure_message Chef::Exceptions::Service, "#{new_resource}: No custom command for #{action} specified and unable to locate the init.d script!" - a.whyrun "Assuming service would be disabled. The init script is not presently installed." + a.whyrun "Assuming service would be enabled. The init script is not presently installed." end end diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index e5635daed3..f1c836614d 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -41,6 +41,7 @@ require "chef/provider/log" require "chef/provider/ohai" require "chef/provider/mdadm" require "chef/provider/mount" +require "chef/provider/noop" require "chef/provider/package" require "chef/provider/powershell_script" require "chef/provider/osx_profile" diff --git a/lib/chef/resource_builder.rb b/lib/chef/resource_builder.rb index f3ca2e95ad..138e401d5c 100644 --- a/lib/chef/resource_builder.rb +++ b/lib/chef/resource_builder.rb @@ -137,7 +137,7 @@ class Chef @prior_resource ||= begin key = "#{type}[#{name}]" - run_context.resource_collection.lookup(key) + run_context.resource_collection.lookup_local(key) rescue Chef::Exceptions::ResourceNotFound nil end diff --git a/lib/chef/resource_collection.rb b/lib/chef/resource_collection.rb index 6c5b4289d8..1429c25d7f 100644 --- a/lib/chef/resource_collection.rb +++ b/lib/chef/resource_collection.rb @@ -33,9 +33,12 @@ class Chef extend Forwardable attr_reader :resource_set, :resource_list - private :resource_set, :resource_list + attr_accessor :run_context - def initialize + protected :resource_set, :resource_list + + def initialize(run_context = nil) + @run_context = run_context @resource_set = ResourceSet.new @resource_list = ResourceList.new end @@ -79,11 +82,50 @@ class Chef resource_list_methods = Enumerable.instance_methods + [:iterator, :all_resources, :[], :each, :execute_each_resource, :each_index, :empty?] - - [:find] # find needs to run on the set - resource_set_methods = [:lookup, :find, :resources, :keys, :validate_lookup_spec!] + [:find] # find overridden below + resource_set_methods = [:resources, :keys, :validate_lookup_spec!] def_delegators :resource_list, *resource_list_methods def_delegators :resource_set, *resource_set_methods + def lookup_local(key) + resource_set.lookup(key) + end + + def find_local(*args) + resource_set.find(*args) + end + + def lookup(key) + if run_context.nil? + lookup_local(key) + else + lookup_recursive(run_context, key) + end + end + + def find(*args) + if run_context.nil? + find_local(*args) + else + find_recursive(run_context, *args) + end + end + + private + + def lookup_recursive(rc, key) + rc.resource_collection.resource_set.lookup(key) + rescue Chef::Exceptions::ResourceNotFound + raise if rc.parent_run_context.nil? + lookup_recursive(rc.parent_run_context, key) + end + + def find_recursive(rc, *args) + rc.resource_collection.resource_set.find(*args) + rescue Chef::Exceptions::ResourceNotFound + raise if rc.parent_run_context.nil? + find_recursive(rc.parent_run_context, *args) + end end end diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb index cb3338d3de..29c936a932 100644 --- a/lib/chef/run_context.rb +++ b/lib/chef/run_context.rb @@ -130,6 +130,14 @@ class Chef # attr_reader :delayed_notification_collection + # + # An Array containing the delayed (end of run) notifications triggered by + # resources during the converge phase of the chef run. + # + # @return [Array[Chef::Resource::Notification]] An array of notification objects + # + attr_reader :delayed_actions + # Creates a new Chef::RunContext object and populates its fields. This object gets # used by the Chef Server to generate a fully compiled recipe list for a node. # @@ -152,6 +160,7 @@ class Chef @loaded_attributes_hash = {} @reboot_info = {} @cookbook_compiler = nil + @delayed_actions = [] initialize_child_state end @@ -172,10 +181,11 @@ class Chef # def initialize_child_state @audits = {} - @resource_collection = Chef::ResourceCollection.new + @resource_collection = Chef::ResourceCollection.new(self) @before_notification_collection = Hash.new { |h, k| h[k] = [] } @immediate_notification_collection = Hash.new { |h, k| h[k] = [] } @delayed_notification_collection = Hash.new { |h, k| h[k] = [] } + @delayed_actions = [] end # @@ -221,6 +231,18 @@ class Chef end # + # Adds a delayed action to the +delayed_actions+. + # + def add_delayed_action(notification) + if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) } + Chef::Log.info( "#{notification.notifying_resource} not queuing delayed action #{notification.action} on #{notification.resource}"\ + " (delayed), as it's already been queued") + else + delayed_actions << notification + end + end + + # # Get the list of before notifications sent by the given resource. # # TODO seriously, this is actually wrong. resource.name is not unique, @@ -640,6 +662,8 @@ ERROR_MESSAGE audits audits= create_child + add_delayed_action + delayed_actions delayed_notification_collection delayed_notification_collection= delayed_notifications diff --git a/lib/chef/runner.rb b/lib/chef/runner.rb index ce128203f2..cd5615bcec 100644 --- a/lib/chef/runner.rb +++ b/lib/chef/runner.rb @@ -30,13 +30,14 @@ class Chef attr_reader :run_context - attr_reader :delayed_actions - include Chef::Mixin::ParamsValidate def initialize(run_context) - @run_context = run_context - @delayed_actions = [] + @run_context = run_context + end + + def delayed_actions + @run_context.delayed_actions end def events @@ -48,16 +49,11 @@ class Chef def run_action(resource, action, notification_type = nil, notifying_resource = nil) # If there are any before notifications, why-run the resource # and notify anyone who needs notifying - # TODO cheffish has a bug where it passes itself instead of the run_context to us, so doesn't have before_notifications. Fix there, update dependency requirement, and remove this if statement. - before_notifications = run_context.before_notifications(resource) if run_context.respond_to?(:before_notifications) - if before_notifications && !before_notifications.empty? - whyrun_before = Chef::Config[:why_run] - begin - Chef::Config[:why_run] = true + before_notifications = run_context.before_notifications(resource) || [] + unless before_notifications.empty? + forced_why_run do Chef::Log.info("#{resource} running why-run #{action} action to support before action") resource.run_action(action, notification_type, notifying_resource) - ensure - Chef::Config[:why_run] = whyrun_before end if resource.updated_by_last_action? @@ -65,8 +61,8 @@ class Chef Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (before)") run_action(notification.resource, notification.action, :before, resource) end + resource.updated_by_last_action(false) end - end # Actually run the action for realsies @@ -82,12 +78,8 @@ class Chef end run_context.delayed_notifications(resource).each do |notification| - if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) } - Chef::Log.info( "#{resource} not queuing delayed action #{notification.action} on #{notification.resource}"\ - " (delayed), as it's already been queued") - else - delayed_actions << notification - end + # send the notification to the run_context of the receiving resource + notification.resource.run_context.add_delayed_action(notification) end end end @@ -137,5 +129,15 @@ class Chef rescue Exception => e e end + + # helper to run a block of code with why_run forced to true and then restore it correctly + def forced_why_run + saved = Chef::Config[:why_run] + Chef::Config[:why_run] = true + yield + ensure + Chef::Config[:why_run] = saved + end + end end diff --git a/lib/chef/version.rb b/lib/chef/version.rb index be629e58e4..e81011764c 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -21,7 +21,7 @@ class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.8.6" + VERSION = "12.9.29" end # diff --git a/lib/chef/win32/registry.rb b/lib/chef/win32/registry.rb index bccd2e3c72..613994295c 100644 --- a/lib/chef/win32/registry.rb +++ b/lib/chef/win32/registry.rb @@ -61,7 +61,9 @@ class Chef end def set_value(key_path, value) - Chef::Log.debug("Updating value #{value[:name]} in registry key #{key_path} with type #{value[:type]} and data #{value[:data]}") + data = value[:data] + data = data.to_s if value[:type] == :string + Chef::Log.debug("Updating value #{value[:name]} in registry key #{key_path} with type #{value[:type]} and data #{data}") key_exists!(key_path) hive, key = get_hive_and_key(key_path) if value_exists?(key_path, value) @@ -70,13 +72,13 @@ class Chef return false else hive.open(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry_system_architecture) do |reg| - reg.write(value[:name], get_type_from_name(value[:type]), value[:data]) + reg.write(value[:name], get_type_from_name(value[:type]), data) end Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} updated") end else hive.open(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry_system_architecture) do |reg| - reg.write(value[:name], get_type_from_name(value[:type]), value[:data]) + reg.write(value[:name], get_type_from_name(value[:type]), data) end Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} created") end diff --git a/omnibus/Gemfile b/omnibus/Gemfile index 07d1e0c9f3..295fb2f490 100644 --- a/omnibus/Gemfile +++ b/omnibus/Gemfile @@ -7,6 +7,12 @@ gem "omnibus-software", git: "https://github.com/chef/omnibus-software.git" # bundler very unhappy. Remove this when upstream has merged zed-0xff/pedump#6 . gem "pedump", git: "https://github.com/ksubrama/pedump.git", branch: "patch-1" +# `json_pure` has a bug in it that is failing Chef builds. We include the +# json gem into the Gemfile so that running `bundle exec` will have +# this gem on the load path, and cause `require 'json/ext'` to succeed. This +# prevents loading the `json_pure` gem +gem "json", ">= 1.8.1" + # This development group is installed by default when you run `bundle install`, # but if you are using Omnibus in a CI-based infrastructure, you do not need # the Test Kitchen-based build lab. You can skip these unnecessary dependencies @@ -16,9 +22,9 @@ group :development do gem "berkshelf", "~> 3.0" # Use Test Kitchen with Vagrant for converging the build environment - gem "test-kitchen", "~> 1.5.0" + gem "test-kitchen", "~> 1.7.1" gem "kitchen-vagrant", "~> 0.19.0" - gem "winrm-transport", "~> 1.0" + gem "winrm-fs", "~> 0.4.0" gem "pry" gem "pry-byebug" gem "pry-stack_explorer" diff --git a/spec/integration/recipes/noop_resource_spec.rb b/spec/integration/recipes/noop_resource_spec.rb new file mode 100644 index 0000000000..c8ff3e6b5e --- /dev/null +++ b/spec/integration/recipes/noop_resource_spec.rb @@ -0,0 +1,24 @@ +require "support/shared/integration/integration_helper" + +describe "Resources with a no-op provider" do + include IntegrationSupport + + context "with noop provider providing foo" do + before(:context) { + class NoOpFoo < Chef::Resource + resource_name "hi_there" + default_action :update + end + Chef::Provider::Noop.provides :hi_there + } + + it "does not blow up a run with a noop'd resource" do + recipe = converge { + hi_there "blah" do + action :update + end + } + expect(recipe.logged_warnings).to eq "" + end + end +end diff --git a/spec/integration/recipes/notifies_spec.rb b/spec/integration/recipes/notifies_spec.rb new file mode 100644 index 0000000000..000f5e37bf --- /dev/null +++ b/spec/integration/recipes/notifies_spec.rb @@ -0,0 +1,334 @@ +require "support/shared/integration/integration_helper" +require "chef/mixin/shell_out" + +describe "notifications" do + include IntegrationSupport + include Chef::Mixin::ShellOut + + let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) } + let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" } + + when_the_repository "notifies delayed one" do + before do + directory "cookbooks/x" do + + file "resources/notifying_test.rb", <<EOM +default_action :run +provides :notifying_test +resource_name :notifying_test + +action :run do + log "bar" do + notifies :write, 'log[foo]', :delayed + end +end +EOM + + file "recipes/default.rb", <<EOM +log "foo" do + action :nothing +end +notifying_test "whatever" +log "baz" +EOM + + end + end + + it "should complete with success" do + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +log_level :warn +EOM + + result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir) + # our delayed notification should run at the end of the parent run_context after the baz resource + expect(result.stdout).to match(/\* log\[bar\] action write\s+\* log\[baz\] action write\s+\* log\[foo\] action write/) + result.error! + end + end + + when_the_repository "notifies delayed two" do + before do + directory "cookbooks/x" do + + file "resources/notifying_test.rb", <<EOM +default_action :run +provides :notifying_test +resource_name :notifying_test + +action :run do + log "bar" do + notifies :write, 'log[foo]', :delayed + end +end +EOM + + file "recipes/default.rb", <<EOM +log "foo" do + action :nothing +end +notifying_test "whatever" +log "baz" do + notifies :write, 'log[foo]', :delayed +end +EOM + + end + end + + it "should complete with success" do + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +log_level :warn +EOM + + result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir) + # our delayed notification should run at the end of the parent run_context after the baz resource + expect(result.stdout).to match(/\* log\[bar\] action write\s+\* log\[baz\] action write\s+\* log\[foo\] action write/) + # and only run once + expect(result.stdout).not_to match(/\* log\[foo\] action write.*\* log\[foo\] action write/) + result.error! + end + end + + when_the_repository "notifies delayed three" do + before do + directory "cookbooks/x" do + + file "resources/notifying_test.rb", <<EOM +default_action :run +provides :notifying_test +resource_name :notifying_test + +action :run do + log "bar" do + notifies :write, 'log[foo]', :delayed + end +end +EOM + + file "recipes/default.rb", <<EOM +log "foo" do + action :nothing +end +log "quux" do + notifies :write, 'log[foo]', :delayed + notifies :write, 'log[baz]', :delayed +end +notifying_test "whatever" +log "baz" +EOM + + end + end + + it "should complete with success" do + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +log_level :warn +EOM + + result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir) + # the delayed notification from the sub-resource is de-duplicated by the notification already in the parent run_context + expect(result.stdout).to match(/\* log\[quux\] action write\s+\* notifying_test\[whatever\] action run\s+\* log\[bar\] action write\s+\* log\[baz\] action write\s+\* log\[foo\] action write\s+\* log\[baz\] action write/) + # and only run once + expect(result.stdout).not_to match(/\* log\[foo\] action write.*\* log\[foo\] action write/) + result.error! + end + end + + when_the_repository "notifies delayed four" do + before do + directory "cookbooks/x" do + file "recipes/default.rb", <<EOM +log "foo" do + action :nothing +end +log "bar" do + notifies :write, 'log[foo]', :delayed +end +log "baz" do + notifies :write, 'log[foo]', :delayed +end +EOM + + end + end + + it "should complete with success" do + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +log_level :warn +EOM + + result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir) + # the delayed notification from the sub-resource is de-duplicated by the notification already in the parent run_context + expect(result.stdout).to match(/\* log\[bar\] action write\s+\* log\[baz\] action write\s+\* log\[foo\] action write/) + # and only run once + expect(result.stdout).not_to match(/\* log\[foo\] action write.*\* log\[foo\] action write/) + result.error! + end + end + + when_the_repository "notifies immediately" do + before do + directory "cookbooks/x" do + + file "resources/notifying_test.rb", <<EOM +default_action :run +provides :notifying_test +resource_name :notifying_test + +action :run do + log "bar" do + notifies :write, 'log[foo]', :immediately + end +end +EOM + + file "recipes/default.rb", <<EOM +log "foo" do + action :nothing +end +notifying_test "whatever" +log "baz" +EOM + + end + end + + it "should complete with success" do + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +log_level :warn +EOM + + result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir) + expect(result.stdout).to match(/\* log\[bar\] action write\s+\* log\[foo\] action write\s+\* log\[baz\] action write/) + result.error! + end + end + + when_the_repository "uses old notifies syntax" do + before do + directory "cookbooks/x" do + + file "resources/notifying_test.rb", <<EOM +default_action :run +provides :notifying_test +resource_name :notifying_test + +action :run do + log "bar" do + notifies :write, resources(log: "foo"), :immediately + end +end +EOM + + file "recipes/default.rb", <<EOM +log "foo" do + action :nothing +end +notifying_test "whatever" +log "baz" +EOM + + end + end + + it "should complete with success" do + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +log_level :warn +EOM + + result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir) + expect(result.stdout).to match(/\* log\[bar\] action write\s+\* log\[foo\] action write\s+\* log\[baz\] action write/) + result.error! + end + end + + when_the_repository "does not have a matching resource" do + before do + directory "cookbooks/x" do + + file "resources/notifying_test.rb", <<EOM +default_action :run +provides :notifying_test +resource_name :notifying_test + +action :run do + log "bar" do + notifies :write, "log[foo]" + end +end +EOM + + file "recipes/default.rb", <<EOM +notifying_test "whatever" +log "baz" +EOM + + end + end + + it "should complete with success" do + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +log_level :warn +EOM + + result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir) + expect(result.stdout).to match(/Chef::Exceptions::ResourceNotFound/) + expect(result.exitstatus).not_to eql(0) + end + end + + when_the_repository "encounters identical resources in parent and child resource collections" do + before do + directory "cookbooks/x" do + + file "resources/cloning_test.rb", <<EOM +default_action :run +provides :cloning_test +resource_name :cloning_test + +action :run do + log "bar" do + level :info + end +end +EOM + + file "recipes/default.rb", <<EOM +log "bar" do + level :warn +end + +cloning_test "whatever" +EOM + + end + end + + it "should complete with success" do + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +log_level :warn +EOM + + result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'x::default'", :cwd => chef_dir) + expect(result.stdout).not_to match(/CHEF-3694/) + result.error! + end + end +end diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb index 97a297ccb5..6765ca93ae 100644 --- a/spec/unit/application/client_spec.rb +++ b/spec/unit/application/client_spec.rb @@ -17,6 +17,55 @@ require "spec_helper" +shared_context "with signal handlers" do + before do + Chef::Config[:specific_recipes] = [] # normally gets set in @app.reconfigure + + @app = Chef::Application::Client.new + @app.setup_signal_handlers + # Default logger doesn't work correctly when logging from a trap handler. + @app.configure_logging + end +end + +shared_context "with interval_sleep" do + before do + run_count = 0 + + # uncomment to debug failures... + # Chef::Log.init($stderr) + # Chef::Log.level = :debug + + allow(@app).to receive(:run_chef_client) do + run_count += 1 + if run_count > 3 + exit 0 + end + + # If everything is fine, sending USR1 to self should prevent + # app to go into splay sleep forever. + Process.kill("USR1", Process.pid) + # On Ruby < 2.1, we need to give the signal handlers a little + # more time, otherwise the test will fail because interleavings. + sleep 1 + end + + number_of_sleep_calls = 0 + + # This is a very complicated way of writing + # @app.should_receive(:sleep).once. + # We have to do it this way because the main loop of + # Chef::Application::Client swallows most exceptions, and we need to be + # able to expose our expectation failures to the parent process in the test. + allow(@app).to receive(:interval_sleep) do |arg| + number_of_sleep_calls += 1 + if number_of_sleep_calls > 1 + exit 127 + end + end + end +end + describe Chef::Application::Client, "reconfigure" do let(:app) do a = described_class.new @@ -26,6 +75,7 @@ describe Chef::Application::Client, "reconfigure" do before do allow(Kernel).to receive(:trap).and_return(:ok) + allow(::File).to receive(:read).and_call_original allow(::File).to receive(:read).with(Chef::Config.platform_specific_path("/etc/chef/client.rb")).and_return("") @original_argv = ARGV.dup @@ -52,17 +102,44 @@ describe Chef::Application::Client, "reconfigure" do app.reconfigure end - context "when given a named_run_list" do + shared_examples "sets the configuration" do |cli_arguments, expected_config| + describe cli_arguments do + before do + ARGV.replace(cli_arguments.split) + app.reconfigure + end - before do - ARGV.replace( %w{ --named-run-list arglebargle-example } ) - app.reconfigure + it "sets #{expected_config}" do + expect(Chef::Config.configuration).to include expected_config + end + end + end + + describe "--named-run-list" do + it_behaves_like "sets the configuration", + "--named-run-list arglebargle-example", + :named_run_list => "arglebargle-example" + end + + describe "--no-listen" do + it_behaves_like "sets the configuration", "--no-listen", :listen => false + end + + describe "--daemonize", :unix_only do + context "with no value" do + it_behaves_like "sets the configuration", "--daemonize", + :daemonize => true end - it "sets named_run_list in Chef::Config" do - expect(Chef::Config[:named_run_list]).to eq("arglebargle-example") + context "with an integer value" do + it_behaves_like "sets the configuration", "--daemonize 5", + :daemonize => 5 end + context "with a non-integer value" do + it_behaves_like "sets the configuration", "--daemonize foo", + :daemonize => true + end end end @@ -116,15 +193,46 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config end end - describe "when in daemonized mode and no interval has been set" do + describe "daemonized mode", :unix_only do + let(:daemonize) { true } + before do - Chef::Config[:daemonize] = true - Chef::Config[:interval] = nil + Chef::Config[:daemonize] = daemonize + allow(Chef::Daemon).to receive(:daemonize) end - it "should set the interval to 1800" do - app.reconfigure - expect(Chef::Config.interval).to eq(1800) + context "when no interval has been set" do + before do + Chef::Config[:interval] = nil + end + + it "should set the interval to 1800" do + app.reconfigure + expect(Chef::Config.interval).to eq(1800) + end + end + + context "when the daemonize option is an integer" do + include_context "with signal handlers" + include_context "with interval_sleep" + + let(:wait_secs) { 1 } + let(:daemonize) { wait_secs } + + before do + allow(@app).to receive(:interval_sleep).with(wait_secs).and_return true + allow(@app).to receive(:interval_sleep).with(0).and_call_original + end + + it "sleeps for the amount of time passed" do + pid = fork do + expect(@app).to receive(:interval_sleep).with(wait_secs) + @app.run_application + end + _pid, result = Process.waitpid2(pid) + + expect(result.exitstatus).to eq 0 + end end end @@ -148,16 +256,6 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config end - describe "when --no-listen is set" do - - it "configures listen = false" do - app.config[:listen] = false - app.reconfigure - expect(Chef::Config[:listen]).to eq(false) - end - - end - describe "when the json_attribs configuration option is specified" do let(:json_attribs) { { "a" => "b" } } @@ -305,14 +403,9 @@ describe Chef::Application::Client, "configure_chef" do end describe Chef::Application::Client, "run_application", :unix_only do - before(:each) do - Chef::Config[:specific_recipes] = [] # normally gets set in @app.reconfigure - - @app = Chef::Application::Client.new - @app.setup_signal_handlers - # Default logger doesn't work correctly when logging from a trap handler. - @app.configure_logging + include_context "with signal handlers" + before(:each) do @pipe = IO.pipe @client = Chef::Client.new allow(Chef::Client).to receive(:new).and_return(@client) @@ -383,44 +476,11 @@ describe Chef::Application::Client, "run_application", :unix_only do end describe "when splay is set" do + include_context "with interval_sleep" + before do Chef::Config[:splay] = 10 Chef::Config[:interval] = 10 - - run_count = 0 - - # uncomment to debug failures... - # Chef::Log.init($stderr) - # Chef::Log.level = :debug - - allow(@app).to receive(:run_chef_client) do - - run_count += 1 - if run_count > 3 - exit 0 - end - - # If everything is fine, sending USR1 to self should prevent - # app to go into splay sleep forever. - Process.kill("USR1", Process.pid) - # On Ruby < 2.1, we need to give the signal handlers a little - # more time, otherwise the test will fail because interleavings. - sleep 1 - end - - number_of_sleep_calls = 0 - - # This is a very complicated way of writing - # @app.should_receive(:sleep).once. - # We have to do it this way because the main loop of - # Chef::Application::Client swallows most exceptions, and we need to be - # able to expose our expectation failures to the parent process in the test. - allow(@app).to receive(:interval_sleep) do |arg| - number_of_sleep_calls += 1 - if number_of_sleep_calls > 1 - exit 127 - end - end end it "shouldn't sleep when sent USR1" do diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb index 462b7314c4..8ab6e13204 100644 --- a/spec/unit/application_spec.rb +++ b/spec/unit/application_spec.rb @@ -247,6 +247,33 @@ describe Chef::Application do it_behaves_like "log_level_is_auto" end + + describe "log_location" do + shared_examples("sets log_location") do |config_value, expected_class| + context "when the configured value is #{config_value.inspect}" do + let(:logger_instance) { instance_double(expected_class).as_null_object } + + before do + allow(expected_class).to receive(:new).and_return(logger_instance) + Chef::Config[:log_location] = config_value + end + + it "it sets log_location to an instance of #{expected_class}" do + expect(expected_class).to receive(:new).with no_args + @app.configure_logging + expect(Chef::Config[:log_location]).to be logger_instance + end + end + end + + if Chef::Platform.windows? + it_behaves_like "sets log_location", :win_evt, Chef::Log::WinEvt + it_behaves_like "sets log_location", "win_evt", Chef::Log::WinEvt + else + it_behaves_like "sets log_location", :syslog, Chef::Log::Syslog + it_behaves_like "sets log_location", "syslog", Chef::Log::Syslog + end + end end end diff --git a/spec/unit/knife/cookbook_create_spec.rb b/spec/unit/knife/cookbook_create_spec.rb index a183bef103..5e343f3db1 100644 --- a/spec/unit/knife/cookbook_create_spec.rb +++ b/spec/unit/knife/cookbook_create_spec.rb @@ -56,13 +56,13 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", + :cookbook_copyright => "Chef Software, Inc.", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "none") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "YOUR_EMAIL", "none", "md") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "YOUR_EMAIL", "none", "md") @knife.run end @@ -70,14 +70,14 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "none") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "none", "md") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "none", "md") @knife.run end @@ -85,15 +85,15 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => "apachev2", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "apachev2") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "apachev2") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "apachev2", "md") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "apachev2", "md") @knife.run end @@ -101,15 +101,15 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => false, } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "none") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "none", "md") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "none", "md") @knife.run end @@ -117,15 +117,15 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => "false", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "none") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "none") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "none", "md") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "none", "md") @knife.run end @@ -133,15 +133,15 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => "gplv2", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "gplv2") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "gplv2") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "gplv2", "md") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "gplv2", "md") @knife.run end @@ -149,15 +149,15 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => "gplv3", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "gplv3") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "gplv3") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "gplv3", "md") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "gplv3", "md") @knife.run end @@ -165,15 +165,15 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => "mit", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "md") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "md") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "md") @knife.run end @@ -181,33 +181,33 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => "mit", :readme_format => "rdoc", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "rdoc") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "rdoc") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "rdoc") @knife.run end - it "should allow specifying the mkd readme format" do + it "should allow specifying the md readme format" do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => "mit", :readme_format => "mkd", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "mkd") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "mkd") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "mkd") @knife.run end @@ -215,16 +215,16 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => "mit", :readme_format => "txt", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "txt") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "txt") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "txt") @knife.run end @@ -232,16 +232,16 @@ describe Chef::Knife::CookbookCreate do @dir = Dir.tmpdir @knife.config = { :cookbook_path => @dir, - :cookbook_copyright => "Opscode, Inc", - :cookbook_email => "nuo@opscode.com", + :cookbook_copyright => "Chef Software, Inc.", + :cookbook_email => "test@chef.io", :cookbook_license => "mit", :readme_format => "foo", } @knife.name_args = ["foobar"] - expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Opscode, Inc", "mit") + expect(@knife).to receive(:create_cookbook).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "mit") expect(@knife).to receive(:create_readme).with(@dir, @knife.name_args.first, "foo") expect(@knife).to receive(:create_changelog).with(@dir, @knife.name_args.first) - expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Opscode, Inc", "nuo@opscode.com", "mit", "foo") + expect(@knife).to receive(:create_metadata).with(@dir, @knife.name_args.first, "Chef Software, Inc.", "test@chef.io", "mit", "foo") @knife.run end diff --git a/spec/unit/knife/ssh_spec.rb b/spec/unit/knife/ssh_spec.rb index 3a8728515d..65cc5a97b4 100644 --- a/spec/unit/knife/ssh_spec.rb +++ b/spec/unit/knife/ssh_spec.rb @@ -203,6 +203,23 @@ describe Chef::Knife::Ssh do expect(@knife.session.servers[0].port).to eq(123) end + it "uses the timeout from Chef Config" do + Chef::Config[:knife][:ssh_timeout] = 5 + @knife.session_from_list([["the.b.org", nil]]) + expect(@knife.session.servers[0].options[:timeout]).to eq(5) + end + + it "uses the timeout from knife config" do + @knife.config[:ssh_timeout] = 6 + @knife.session_from_list([["the.b.org", nil]]) + expect(@knife.session.servers[0].options[:timeout]).to eq(6) + end + + it "defaults to no timeout" do + @knife.session_from_list([["the.b.org", nil]]) + expect(@knife.session.servers[0].options[:timeout]).to eq(nil) + end + it "uses the user from an ssh config file" do @knife.session_from_list([["the.b.org", 123]]) expect(@knife.session.servers[0].user).to eq("locutus") diff --git a/spec/unit/mixin/path_sanity_spec.rb b/spec/unit/mixin/path_sanity_spec.rb index 2c26e2fb79..675b5722be 100644 --- a/spec/unit/mixin/path_sanity_spec.rb +++ b/spec/unit/mixin/path_sanity_spec.rb @@ -57,7 +57,7 @@ describe Chef::Mixin::PathSanity do end it "creates path with utf-8 encoding" do - env = { "PATH" => "/usr/bin:/sbin:/bin:/b\x81t".force_encoding("ISO-8859-1") } + env = { "PATH" => "/usr/bin:/sbin:/bin:/b#{0x81.chr}t".force_encoding("ISO-8859-1") } @sanity.enforce_path_sanity(env) expect(env["PATH"].encoding.to_s).to eq("UTF-8") end diff --git a/spec/unit/mixin/xml_escape_spec.rb b/spec/unit/mixin/xml_escape_spec.rb index 2723fce765..903c91164e 100644 --- a/spec/unit/mixin/xml_escape_spec.rb +++ b/spec/unit/mixin/xml_escape_spec.rb @@ -49,6 +49,6 @@ describe Chef::Mixin::XMLEscape do end it "converts win 1252 characters correctly" do - expect(@escaper.xml_escape("\x80")).to eq('€') + expect(@escaper.xml_escape("#{0x80.chr}")).to eq('€') end end diff --git a/spec/unit/provider/mount/aix_spec.rb b/spec/unit/provider/mount/aix_spec.rb index 9a34a6d21d..3371c270c5 100644 --- a/spec/unit/provider/mount/aix_spec.rb +++ b/spec/unit/provider/mount/aix_spec.rb @@ -126,10 +126,9 @@ ENABLED @provider.run_action(:mount) end - it "should not mount resource if it is already mounted and the options have not changed" do + it "should not mount resource if it is already mounted" do stub_mounted_enabled(@provider, @mounted_output, "") - allow(@provider).to receive(:mount_options_unchanged?).and_return(true) expect(@provider).not_to receive(:mount_fs) @provider.run_action(:mount) diff --git a/spec/unit/provider/mount/mount_spec.rb b/spec/unit/provider/mount/mount_spec.rb index 52be15b2a8..42585d9e3e 100644 --- a/spec/unit/provider/mount/mount_spec.rb +++ b/spec/unit/provider/mount/mount_spec.rb @@ -323,12 +323,6 @@ describe Chef::Provider::Mount::Mount do @provider.mount_fs() end - it "should not mount the filesystem if it is mounted and the options have not changed" do - allow(@current_resource).to receive(:mounted).and_return(true) - expect(@provider).to_not receive(:shell_out!) - @provider.mount_fs() - end - end describe "umount_fs" do diff --git a/spec/unit/provider/mount/windows_spec.rb b/spec/unit/provider/mount/windows_spec.rb index ec1945de50..fdb44836b5 100644 --- a/spec/unit/provider/mount/windows_spec.rb +++ b/spec/unit/provider/mount/windows_spec.rb @@ -112,14 +112,6 @@ describe Chef::Provider::Mount::Windows do @provider.mount_fs end - it "should remount the filesystem if it is mounted and the options have changed" do - expect(@vol).to receive(:add).with(:remote => @new_resource.device, - :username => @new_resource.username, - :domainname => @new_resource.domain, - :password => @new_resource.password) - @provider.mount_fs - end - context "mount_options_unchanged?" do it "should return true if mounted device is the same" do allow(@current_resource).to receive(:device).and_return(GUID) @@ -131,12 +123,6 @@ describe Chef::Provider::Mount::Windows do expect(@provider.send :mount_options_unchanged?).to be false end end - - it "should not mount the filesystem if it is mounted and the options have not changed" do - expect(@vol).to_not receive(:add) - allow(@current_resource).to receive(:mounted).and_return(true) - @provider.mount_fs - end end describe "when unmounting a file system" do diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb index fa168e571e..19967e8496 100644 --- a/spec/unit/provider/mount_spec.rb +++ b/spec/unit/provider/mount_spec.rb @@ -60,25 +60,6 @@ describe Chef::Provider::Mount do provider.run_action(:mount) expect(new_resource).to be_updated_by_last_action end - - it "should remount the filesystem if it is mounted and the options have changed" do - allow(current_resource).to receive(:mounted).and_return(true) - allow(provider).to receive(:mount_options_unchanged?).and_return(false) - expect(provider).to receive(:umount_fs).and_return(true) - expect(provider).to receive(:wait_until_unmounted) - expect(provider).to receive(:mount_fs).and_return(true) - provider.run_action(:mount) - expect(new_resource).to be_updated_by_last_action - end - - it "should not mount the filesystem if it is mounted and the options have not changed" do - allow(current_resource).to receive(:mounted).and_return(true) - expect(provider).to receive(:mount_options_unchanged?).and_return(true) - expect(provider).not_to receive(:mount_fs) - provider.run_action(:mount) - expect(new_resource).not_to be_updated_by_last_action - end - end describe "when the target state is an unmounted filesystem" do diff --git a/spec/unit/provider/service/redhat_spec.rb b/spec/unit/provider/service/redhat_spec.rb index 9b878abcd6..40f9bc3a91 100644 --- a/spec/unit/provider/service/redhat_spec.rb +++ b/spec/unit/provider/service/redhat_spec.rb @@ -157,6 +157,20 @@ describe "Chef::Provider::Service::Redhat" do end end + %w{start reload restart}.each do |action| + it "should not raise an error when the action is #{action} and init_command is set" do + @new_resource.init_command("/etc/init.d/chef") + @provider.action = action + expect { @provider.process_resource_requirements }.not_to raise_error + end + + it "should not raise an error when the action is #{action} and #{action}_command is set" do + @new_resource.send("#{action}_command", "/etc/init.d/chef #{action}") + @provider.action = action + expect { @provider.process_resource_requirements }.not_to raise_error + end + end + %w{stop disable}.each do |action| it "should not raise an error when the action is #{action}" do @provider.action = action diff --git a/spec/unit/resource_collection_spec.rb b/spec/unit/resource_collection_spec.rb index a6ad895c51..256ae090e0 100644 --- a/spec/unit/resource_collection_spec.rb +++ b/spec/unit/resource_collection_spec.rb @@ -283,6 +283,76 @@ describe Chef::ResourceCollection do end end + describe "multiple run_contexts" do + let(:node) { Chef::Node.new } + let(:parent_run_context) { Chef::RunContext.new(node, {}, nil) } + let(:parent_resource_collection) { parent_run_context.resource_collection } + let(:child_run_context) { parent_run_context.create_child } + let(:child_resource_collection) { child_run_context.resource_collection } + + it "should find resources in the parent run_context with lookup" do + zmr = Chef::Resource::ZenMaster.new("dog") + parent_resource_collection << zmr + expect(child_resource_collection.lookup(zmr.to_s)).to eql(zmr) + end + + it "should not find resources in the parent run_context with lookup_local" do + zmr = Chef::Resource::ZenMaster.new("dog") + parent_resource_collection << zmr + expect { child_resource_collection.lookup_local(zmr.to_s) }.to raise_error(Chef::Exceptions::ResourceNotFound) + end + + it "should find resources in the child run_context with lookup_local" do + zmr = Chef::Resource::ZenMaster.new("dog") + child_resource_collection << zmr + expect(child_resource_collection.lookup_local(zmr.to_s)).to eql(zmr) + end + + it "should find resources in the parent run_context with find" do + zmr = Chef::Resource::ZenMaster.new("dog") + parent_resource_collection << zmr + expect(child_resource_collection.find(zmr.to_s)).to eql(zmr) + end + + it "should not find resources in the parent run_context with find_local" do + zmr = Chef::Resource::ZenMaster.new("dog") + parent_resource_collection << zmr + expect { child_resource_collection.find_local(zmr.to_s) }.to raise_error(Chef::Exceptions::ResourceNotFound) + end + + it "should find resources in the child run_context with find_local" do + zmr = Chef::Resource::ZenMaster.new("dog") + child_resource_collection << zmr + expect(child_resource_collection.find_local(zmr.to_s)).to eql(zmr) + end + + it "should not find resources in the child run_context in any way from the parent" do + zmr = Chef::Resource::ZenMaster.new("dog") + child_resource_collection << zmr + expect { parent_resource_collection.find_local(zmr.to_s) }.to raise_error(Chef::Exceptions::ResourceNotFound) + expect { parent_resource_collection.find(zmr.to_s) }.to raise_error(Chef::Exceptions::ResourceNotFound) + expect { parent_resource_collection.lookup_local(zmr.to_s) }.to raise_error(Chef::Exceptions::ResourceNotFound) + expect { parent_resource_collection.lookup(zmr.to_s) }.to raise_error(Chef::Exceptions::ResourceNotFound) + end + + it "should behave correctly when there is an identically named resource in the child and parent" do + a = Chef::Resource::File.new("something") + a.content("foo") + parent_resource_collection << a + b = Chef::Resource::File.new("something") + b.content("bar") + child_resource_collection << b + expect(child_resource_collection.find_local("file[something]").content).to eql("bar") + expect(child_resource_collection.find("file[something]").content).to eql("bar") + expect(child_resource_collection.lookup_local("file[something]").content).to eql("bar") + expect(child_resource_collection.lookup("file[something]").content).to eql("bar") + expect(parent_resource_collection.find_local("file[something]").content).to eql("foo") + expect(parent_resource_collection.find("file[something]").content).to eql("foo") + expect(parent_resource_collection.lookup_local("file[something]").content).to eql("foo") + expect(parent_resource_collection.lookup("file[something]").content).to eql("foo") + end + end + def check_by_names(results, *names) names.each do |res_name| expect(results.detect { |res| res.name == res_name }).not_to eql(nil) diff --git a/spec/unit/util/diff_spec.rb b/spec/unit/util/diff_spec.rb index 29c645bb1d..4bb0abd087 100644 --- a/spec/unit/util/diff_spec.rb +++ b/spec/unit/util/diff_spec.rb @@ -76,7 +76,7 @@ shared_examples_for "a diff util" do describe "when the old_file has binary content" do before do - old_tempfile.write("\x01\xff") + old_tempfile.write("#{0x01.chr}#{0xFF.chr}") old_tempfile.close end @@ -91,7 +91,7 @@ shared_examples_for "a diff util" do describe "when the new_file has binary content" do before do - new_tempfile.write("\x01\xff") + new_tempfile.write("#{0x01.chr}#{0xFF.chr}") new_tempfile.close end @@ -553,8 +553,8 @@ describe Chef::Util::Diff, :uses_diff => true do let(:plain_ascii) { "This is a text file.\nWith more than one line.\nAnd a \tTab.\nAnd lets make sure that other printable chars work too: ~!@\#$%^&*()`:\"<>?{}|_+,./;'[]\\-=\n" } # these are all byte sequences that are illegal in the other encodings... (but they may legally transcode) let(:utf_8) { "testing utf-8 unicode...\n\n\non a new line: \xE2\x80\x93\n" } # unicode em-dash - let(:latin_1) { "It is more metal.\nif you have an \xFDmlaut.\n" } # NB: changed to y-with-diaresis, but i'm American so I don't know the difference - let(:shift_jis) { "I have no idea what this character is:\n \x83\x80.\n" } # seriously, no clue, but \x80 is nice and illegal in other encodings + let(:latin_1) { "It is more metal.\nif you have an #{0xFD.chr}mlaut.\n" } # NB: changed to y-with-diaresis, but i'm American so I don't know the difference + let(:shift_jis) { "I have no idea what this character is:\n #{0x83.chr}#{0x80.chr}.\n" } # seriously, no clue, but \x80 is nice and illegal in other encodings let(:differ) do # subject differ = Chef::Util::Diff.new |