diff options
55 files changed, 867 insertions, 323 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index e53ac8661c..92b1270b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,19 @@ <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ --> -<!-- latest_release 15.0.233 --> -## [v15.0.233](https://github.com/chef/chef/tree/v15.0.233) (2019-04-24) +<!-- latest_release 15.0.237 --> +## [v15.0.237](https://github.com/chef/chef/tree/v15.0.237) (2019-04-29) #### Merged Pull Requests -- Refactor bootstrapping to use train as the transport with full Windows bootstrap support [#8253](https://github.com/chef/chef/pull/8253) ([marcparadise](https://github.com/marcparadise)) +- "chef-client" => #{Chef::Dist::CLIENT} [#8418](https://github.com/chef/chef/pull/8418) ([bobchaos](https://github.com/bobchaos)) <!-- latest_release --> <!-- release_rollup --> ### Changes since latest stable release #### Merged Pull Requests +- "chef-client" => #{Chef::Dist::CLIENT} [#8418](https://github.com/chef/chef/pull/8418) ([bobchaos](https://github.com/bobchaos)) <!-- 15.0.237 --> +- Add the introduced field to snap_package [#8412](https://github.com/chef/chef/pull/8412) ([tas50](https://github.com/tas50)) <!-- 15.0.236 --> +- windows_task: Add start_when_available support [#8420](https://github.com/chef/chef/pull/8420) ([vsingh-msys](https://github.com/vsingh-msys)) <!-- 15.0.235 --> +- Fix for write permissions were not working properly on windows [#8168](https://github.com/chef/chef/pull/8168) ([vijaymmali1990](https://github.com/vijaymmali1990)) <!-- 15.0.234 --> - Refactor bootstrapping to use train as the transport with full Windows bootstrap support [#8253](https://github.com/chef/chef/pull/8253) ([marcparadise](https://github.com/marcparadise)) <!-- 15.0.233 --> - Move ed25519 gems into omnibus [#8410](https://github.com/chef/chef/pull/8410) ([tas50](https://github.com/tas50)) <!-- 15.0.232 --> - Implement new owner/review structure + expand dev docs [#8350](https://github.com/chef/chef/pull/8350) ([tas50](https://github.com/tas50)) <!-- 15.0.231 --> diff --git a/Gemfile.lock b/Gemfile.lock index a4f7a2845a..43b7e06845 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,7 +8,7 @@ GIT GIT remote: https://github.com/chef/ohai.git - revision: 5c70e89388ebc8ddf7d2d7bfd489024b11c0aece + revision: a1ec73298d623d18cbe05c2b53ac57c8cc9beae0 branch: master specs: ohai (15.0.34) @@ -27,12 +27,11 @@ GIT PATH remote: . specs: - chef (15.0.233) + chef (15.0.237) addressable bcrypt_pbkdf (~> 1.0) bundler (>= 1.10) - chef-config (= 15.0.233) - chef-core (~> 0.0.3) + chef-config (= 15.0.237) chef-zero (>= 14.0.11) diff-lcs (~> 1.2, >= 1.2.4) ed25519 (~> 1.2) @@ -54,14 +53,14 @@ PATH plist (~> 3.2) proxifier (~> 1.0) syslog-logger (~> 1.6) + train-core (~> 2.0, >= 2.0.12) tty-screen (~> 0.6) uuidtools (~> 2.1.5) - chef (15.0.233-universal-mingw32) + chef (15.0.237-universal-mingw32) addressable bcrypt_pbkdf (~> 1.0) bundler (>= 1.10) - chef-config (= 15.0.233) - chef-core (~> 0.0.3) + chef-config (= 15.0.237) chef-zero (>= 14.0.11) diff-lcs (~> 1.2, >= 1.2.4) ed25519 (~> 1.2) @@ -84,6 +83,7 @@ PATH plist (~> 3.2) proxifier (~> 1.0) syslog-logger (~> 1.6) + train-core (~> 2.0, >= 2.0.12) tty-screen (~> 0.6) uuidtools (~> 2.1.5) win32-api (~> 1.5.3) @@ -101,7 +101,7 @@ PATH PATH remote: chef-config specs: - chef-config (15.0.233) + chef-config (15.0.237) addressable fuzzyurl mixlib-config (>= 2.2.12, < 4.0) @@ -113,7 +113,7 @@ GEM specs: addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) - appbundler (0.12.4) + appbundler (0.12.5) mixlib-cli (>= 1.4, < 3.0) mixlib-shellout (>= 2.0, < 4.0) ast (2.4.0) @@ -124,20 +124,6 @@ GEM debug_inspector (>= 0.0.1) builder (3.2.3) byebug (11.0.1) - chef-core (0.0.3) - chef-telemetry - mixlib-log - pastel - r18n-desktop - train-core (~> 2.0, >= 2.0.12) - tty-color - tty-cursor - tty-spinner - chef-telemetry (0.1.8) - chef-config - concurrent-ruby (~> 1.0) - ffi-yajl (~> 2.2) - http (~> 2.2) chef-vault (3.4.3) chef-zero (14.0.12) ffi-yajl (~> 2.2) @@ -149,14 +135,11 @@ GEM chef-zero (~> 14.0) net-ssh coderay (1.1.2) - concurrent-ruby (1.1.5) crack (0.4.3) safe_yaml (~> 1.0.0) debug_inspector (0.0.3) diff-lcs (1.3) docile (1.3.1) - domain_name (0.5.20180417) - unf (>= 0.0.5, < 1.0.0) ed25519 (1.2.4) equatable (0.5.0) erubis (2.7.0) @@ -182,15 +165,6 @@ GEM hashie (3.6.0) highline (1.7.10) htmlentities (4.3.4) - http (2.2.2) - addressable (~> 2.3) - http-cookie (~> 1.0) - http-form_data (~> 1.0.1) - http_parser.rb (~> 0.6.0) - http-cookie (1.0.3) - domain_name (~> 0.5) - http-form_data (1.0.3) - http_parser.rb (0.6.0) httpclient (2.8.3) iniparse (1.4.4) inspec-core (4.1.4.preview) @@ -223,7 +197,7 @@ GEM jaro_winkler (1.5.2) json (2.2.0) libyajl2 (1.2.0) - license-acceptance (0.2.13) + license-acceptance (0.2.16) pastel (~> 0.7) tomlrb (~> 1.2) tty-box (~> 0.3) @@ -264,7 +238,7 @@ GEM octokit (4.14.0) sawyer (~> 0.8.0, >= 0.5.3) parallel (1.17.0) - parser (2.6.2.1) + parser (2.6.3.0) ast (~> 2.4.0) parslet (1.8.2) pastel (0.7.2) @@ -286,9 +260,6 @@ GEM binding_of_caller (>= 0.7) pry (>= 0.9.11) public_suffix (3.0.3) - r18n-core (3.2.0) - r18n-desktop (3.2.0) - r18n-core (= 3.2.0) rack (2.0.7) rainbow (3.0.0) rake (12.3.2) @@ -375,17 +346,12 @@ GEM tty-screen (~> 0.6.4) wisper (~> 2.0.0) tty-screen (0.6.5) - tty-spinner (0.9.0) - tty-cursor (~> 0.6.0) tty-table (0.10.0) equatable (~> 0.5.0) necromancer (~> 0.4.0) pastel (~> 0.7.2) strings (~> 0.1.0) tty-screen (~> 0.6.4) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.6) unicode-display_width (1.4.1) unicode_utils (1.4.0) uuidtools (2.1.5) @@ -417,7 +383,7 @@ GEM win32-taskscheduler (2.0.4) ffi structured_warnings - winrm (2.3.1) + winrm (2.3.2) builder (>= 2.1.2) erubis (~> 2.7) gssapi (~> 1.2) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ef2e843bd5..b0913a2a2f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,9 +12,7 @@ Chef 15 release notes will be added here as development progresses. ### copy_properties_from in Custom Resources -### locale resource - -The locale resource now allows setting all possible LC_* environmental variables. +### ed25519 SSH key support ## New Resources @@ -28,9 +26,45 @@ The locale resource now allows setting all possible LC_* environmental variables ### snap_package resource -### Ruby 2.6.2 +## Resource Improvements + +### windows_task + +windows_task now supports the Start When Available option with a new ``start_when_available`` property. + +### locale + +The locale resource now allows setting all possible LC_* environmental variables. + +### directory + +The directory resource now property supports passing ``deny_rights :write`` on Windows nodes. + +### windows_service + +The windows_service resource has been improved to prevent accidently reverting a service back to default settings in a subsequent definition. + +This example will no longer result in the MyApp service reverting to default RunAsUser: +```ruby +windows_service 'MyApp' do + run_as_user 'MyAppsUser' + run_as_password 'MyAppsUserPassword' + startup_type :automatic + delayed_start true + action [:configure, :start] +end + +... + +windows_service 'MyApp' do + startup_type :automatic + action [:configure, :start] +end +``` + +### Ruby 2.6.3 -Chef now ships with Ruby 2.6.2. This new version of Ruby improves performance and includes many new features to make more advanced Chef usage easier. See https://www.rubyguides.com/2018/11/ruby-2-6-new-features/ for a list of some of the new functionality. +Chef now ships with Ruby 2.6.3. This new version of Ruby improves performance and includes many new features to make more advanced Chef usage easier. See https://www.rubyguides.com/2018/11/ruby-2-6-new-features/ for a list of some of the new functionality. ## New Deprecations @@ -1 +1 @@ -15.0.233
\ No newline at end of file +15.0.237
\ No newline at end of file diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index 824de26477..1673164441 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 = "15.0.233".freeze + VERSION = "15.0.237".freeze end # diff --git a/chef.gemspec b/chef.gemspec index 32e12bad41..a9d9ded1cd 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |s| s.required_ruby_version = ">= 2.5.0" s.add_dependency "chef-config", "= #{Chef::VERSION}" - s.add_dependency "chef-core", "~> 0.0.3" + s.add_dependency "train-core", "~> 2.0", ">= 2.0.12" s.add_dependency "mixlib-cli", ">= 1.7", "< 3.0" s.add_dependency "mixlib-log", ">= 2.0.3", "< 4.0" diff --git a/lib/chef.rb b/lib/chef.rb index c58f46debd..8869a5a890 100644 --- a/lib/chef.rb +++ b/lib/chef.rb @@ -18,18 +18,6 @@ require "chef/version" -# Ensure that this loads ahead of anything that -# might cause rubygems to hit Gem.load_yaml, including -# evaluating gemspecs. When load_yaml is invoked, -# it stubs out the YAML::Syck namespace. This causes -# r18n to break, which expects either YAML::Syck to be there -# and fully defined (particularly, the Syck::PrivateType class), -# or for it to not be there at all. -# -# When it's not - because it's a stub - r18n explodes on loading. -# Ensuring chef_core/text and r18n are loaded first prevents this. -require "chef_core/text" - require "chef/nil_argument" require "chef/mash" require "chef/exceptions" diff --git a/lib/chef/application.rb b/lib/chef/application.rb index 0eaf9721d9..549d8b4482 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -28,6 +28,7 @@ require "mixlib/cli" require "tmpdir" require "rbconfig" require "chef/application/exit_code" +require "chef/dist" class Chef class Application @@ -313,7 +314,7 @@ class Chef " finishing converge to exit normally (send SIGINT to terminate immediately)") end - client_solo = chef_config[:solo] ? "chef-solo" : "chef-client" + client_solo = chef_config[:solo] ? "chef-solo" : "#{Chef::Dist::CLIENT}" $0 = "#{client_solo} worker: ppid=#{Process.ppid};start=#{Time.new.strftime("%R:%S")};" begin logger.trace "Forked instance now converging" diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb index 457cb1578c..5655b8cdfd 100644 --- a/lib/chef/application/solo.rb +++ b/lib/chef/application/solo.rb @@ -324,7 +324,7 @@ class Chef::Application::Solo < Chef::Application def interval_run_chef_client if Chef::Config[:daemonize] - Chef::Daemon.daemonize("chef-client") + Chef::Daemon.daemonize("#{Chef::Dist::CLIENT}") end loop do diff --git a/lib/chef/application/windows_service.rb b/lib/chef/application/windows_service.rb index 6fbb1a36a2..3eb4f63129 100644 --- a/lib/chef/application/windows_service.rb +++ b/lib/chef/application/windows_service.rb @@ -57,7 +57,7 @@ class Chef option :interval, short: "-i SECONDS", long: "--interval SECONDS", - description: "Set the number of seconds to wait between chef-client runs", + description: "Set the number of seconds to wait between #{Chef::Dist::CLIENT} runs", proc: lambda { |s| s.to_i } DEFAULT_LOG_LOCATION ||= "#{ENV['SYSTEMDRIVE']}/chef/client.log".freeze diff --git a/lib/chef/cookbook/synchronizer.rb b/lib/chef/cookbook/synchronizer.rb index a1d02bac65..5f098a0538 100644 --- a/lib/chef/cookbook/synchronizer.rb +++ b/lib/chef/cookbook/synchronizer.rb @@ -17,6 +17,7 @@ require "chef/client" require "chef/util/threaded_job_queue" require "chef/server_api" require "singleton" +require "chef/dist" class Chef @@ -63,7 +64,7 @@ class Chef # manifest. cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_filename| unless @valid_cache_entries[cache_filename] - Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by chef-client.") + Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by #{Chef::Dist::CLIENT}.") cache.delete(cache_filename) end end diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index 0a6c7dc635..4ab9434906 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -18,6 +18,7 @@ # limitations under the License. require "chef-config/exceptions" +require "chef/dist" class Chef # == Chef::Exceptions @@ -401,7 +402,7 @@ class Chef def initialize(response_length, content_length) super <<~EOF Response body length #{response_length} does not match HTTP Content-Length header #{content_length}. - This error is most often caused by network issues (proxies, etc) outside of chef-client. + This error is most often caused by network issues (proxies, etc) outside of #{Chef::Dist::CLIENT}. EOF end end diff --git a/lib/chef/file_access_control/windows.rb b/lib/chef/file_access_control/windows.rb index 2c6b69c257..a7cefdc6d7 100644 --- a/lib/chef/file_access_control/windows.rb +++ b/lib/chef/file_access_control/windows.rb @@ -90,11 +90,13 @@ class Chef target_acl.each do |target_ace| if target_ace.flags & INHERIT_ONLY_ACE == 0 self_ace = target_ace.dup - self_ace.flags = 0 + # We need flag value which is already being set in case of WRITE permissions as 3, so we will not be overwriting it with the hard coded value. + self_ace.flags = 0 unless target_ace.mask == Chef::ReservedNames::Win32::API::Security::WRITE self_ace.mask = securable_object.predict_rights_mask(target_ace.mask) new_target_acl << self_ace end - if target_ace.flags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE) != 0 + # As there is no inheritence needed in case of WRITE permissions. + if target_ace.mask != Chef::ReservedNames::Win32::API::Security::WRITE && target_ace.flags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE) != 0 children_ace = target_ace.dup children_ace.flags |= INHERIT_ONLY_ACE new_target_acl << children_ace @@ -220,7 +222,7 @@ class Chef when :read_execute mask |= GENERIC_READ | GENERIC_EXECUTE when :write - mask |= GENERIC_WRITE + mask |= WRITE else # Otherwise, assume it's an integer specifying the actual flags mask |= permission diff --git a/lib/chef/formatters/error_inspectors/api_error_formatting.rb b/lib/chef/formatters/error_inspectors/api_error_formatting.rb index 4ecdec7105..e74c353ba9 100644 --- a/lib/chef/formatters/error_inspectors/api_error_formatting.rb +++ b/lib/chef/formatters/error_inspectors/api_error_formatting.rb @@ -17,6 +17,7 @@ # require "chef/http/authenticator" +require "chef/dist" class Chef module Formatters @@ -39,7 +40,7 @@ class Chef def describe_eof_error(error_description) error_description.section("Authentication Error:", <<~E) Received an EOF on transport socket. This almost always indicates a network - error external to chef-client. Some causes include: + error external to #{Chef::Dist::CLIENT}. Some causes include: - Blocking ICMP Dest Unreachable (breaking Path MTU Discovery) - IPsec or VPN tunnelling / TCP Encapsulation MTU issues diff --git a/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb b/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb index 9617f729c1..8a94529237 100644 --- a/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb +++ b/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb @@ -17,6 +17,7 @@ # require "chef/formatters/error_inspectors/api_error_formatting" +require "chef/dist" class Chef module Formatters @@ -45,7 +46,7 @@ class Chef when Chef::Exceptions::PrivateKeyMissing error_description.section("Private Key Not Found:", <<~E) Your private key could not be loaded. If the key file exists, ensure that it is - readable by chef-client. + readable by #{Chef::Dist::CLIENT}. E error_description.section("Relevant Config Settings:", <<~E) client_key "#{api_key}" diff --git a/lib/chef/formatters/error_inspectors/registration_error_inspector.rb b/lib/chef/formatters/error_inspectors/registration_error_inspector.rb index 002870abeb..5ba5eba39d 100644 --- a/lib/chef/formatters/error_inspectors/registration_error_inspector.rb +++ b/lib/chef/formatters/error_inspectors/registration_error_inspector.rb @@ -1,7 +1,8 @@ +require "chef/dist" + class Chef module Formatters module ErrorInspectors - # == RegistrationErrorInspector # Wraps exceptions that occur during the client registration process and # suggests possible causes. @@ -38,7 +39,7 @@ class Chef when Chef::Exceptions::PrivateKeyMissing error_description.section("Private Key Not Found:", <<~E) Your private key could not be loaded. If the key file exists, ensure that it is - readable by chef-client. + readable by #{Chef::Dist::CLIENT}. E error_description.section("Relevant Config Settings:", <<~E) validation_key "#{api_key}" diff --git a/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb b/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb index 1cc147db7a..367bba321e 100644 --- a/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb +++ b/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb @@ -16,6 +16,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +require "chef/dist" class Chef module Formatters @@ -55,7 +56,7 @@ class Chef require "chef/win32/security" if !Chef::ReservedNames::Win32::Security.has_admin_privileges? - error_description.section("Missing Windows Admin Privileges", "chef-client doesn't have administrator privileges. This can be a possible reason for the resource failure.") + error_description.section("Missing Windows Admin Privileges", "#{Chef::Dist::CLIENT} doesn't have administrator privileges. This can be a possible reason for the resource failure.") end end end diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb index b68fb1aa1e..d46bf455e9 100644 --- a/lib/chef/knife/bootstrap.rb +++ b/lib/chef/knife/bootstrap.rb @@ -18,10 +18,6 @@ require "chef/knife" require "chef/knife/data_bag_secret_options" -require "erubis" -require "chef/knife/bootstrap/chef_vault_handler" -require "chef/knife/bootstrap/client_builder" -require "chef/util/path_helper" class Chef class Knife @@ -251,14 +247,14 @@ class Chef option :first_boot_attributes, short: "-j JSON_ATTRIBS", long: "--json-attributes", - description: "A JSON string to be added to the first run of chef-client", + description: "A JSON string to be added to the first run of #{Chef::Dist::CLIENT}", proc: lambda { |o| Chef::JSONCompat.parse(o) }, default: nil # bootstrap template option :first_boot_attributes_from_file, long: "--json-attribute-file FILE", - description: "A JSON file to be used to the first run of chef-client", + description: "A JSON file to be used to the first run of #{Chef::Dist::CLIENT}", proc: lambda { |o| Chef::JSONCompat.parse(File.read(o)) }, default: nil @@ -296,25 +292,25 @@ class Chef # bootstrap override: Do this instead of our own setup.sh from omnitruck. Causes bootstrap_url to be ignored. option :bootstrap_install_command, long: "--bootstrap-install-command COMMANDS", - description: "Custom command to install chef-client", + description: "Custom command to install #{Chef::Dist::CLIENT}", proc: Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic } # bootstrap template: Run this command first in the bootstrap script option :bootstrap_preinstall_command, long: "--bootstrap-preinstall-command COMMANDS", - description: "Custom commands to run before installing chef-client", + description: "Custom commands to run before installing #{Chef::Dist::CLIENT}", proc: Proc.new { |preic| Chef::Config[:knife][:bootstrap_preinstall_command] = preic } # bootstrap template option :bootstrap_wget_options, long: "--bootstrap-wget-options OPTIONS", - description: "Add options to wget when installing chef-client", + description: "Add options to wget when installing #{Chef::Dist::CLIENT}", proc: Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo } # bootstrap template option :bootstrap_curl_options, long: "--bootstrap-curl-options OPTIONS", - description: "Add options to curl when install chef-client", + description: "Add options to curl when install #{Chef::Dist::CLIENT}", proc: Proc.new { |co| Chef::Config[:knife][:bootstrap_curl_options] = co } # chef_vault_handler @@ -386,14 +382,16 @@ class Chef attr_accessor :client_builder attr_accessor :chef_vault_handler - attr_reader :target_host + attr_reader :connection deps do + require "erubis" + require "chef/json_compat" - require "tempfile" - require "chef_core/text" # i18n and standardized error structures - require "chef_core/target_host" - require "chef_core/target_resolver" + require "chef/util/path_helper" + require "chef/knife/bootstrap/chef_vault_handler" + require "chef/knife/bootstrap/client_builder" + require "chef/knife/bootstrap/train_connector" end banner "knife bootstrap [PROTOCOL://][USER@]FQDN (options)" @@ -416,8 +414,8 @@ class Chef # # @return [String] Default bootstrap template def default_bootstrap_template - if target_host.base_os == :windows - "windows-chef-client-msi" + if connection.windows? + "windows-#{Chef::Dist::CLIENT}-msi" else "chef-full" end @@ -481,11 +479,11 @@ class Chef end # Establish bootstrap context for template rendering. - # Requires target_host to be a live connection in order to determine + # Requires connection to be a live connection in order to determine # the correct platform. def bootstrap_context @bootstrap_context ||= - if target_host.base_os == :windows + if connection.windows? require "chef/knife/core/windows_bootstrap_context" Knife::Core::WindowsBootstrapContext.new(config, config[:run_list], Chef::Config, secret) else @@ -555,17 +553,12 @@ class Chef bootstrap_path = upload_bootstrap(content) perform_bootstrap(bootstrap_path) ensure - target_host.del_file(bootstrap_path) if target_host && bootstrap_path + connection.del_file!(bootstrap_path) if connection && bootstrap_path end def register_client # chef-vault integration must use the new client-side hawtness, otherwise to use the # new client-side hawtness, just delete your validation key. - # 2019-04-01 TODO - # TODO - should this raise if config says to use vault because json/file/item exists - # but we still have a validation key? That means we can't use the new client hawtness, - # but we also don't tell the operator that their requested vault operations - # won't be performed if chef_vault_handler.doing_chef_vault? || (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key]))) @@ -588,8 +581,8 @@ class Chef def perform_bootstrap(remote_bootstrap_script_path) ui.info("Bootstrapping #{ui.color(server_name, :bold)}") cmd = bootstrap_command(remote_bootstrap_script_path) - r = target_host.run_command(cmd) do |data| - ui.msg("#{ui.color(" [#{target_host.hostname}]", :cyan)} #{data}") + r = connection.run_command(cmd) do |data| + ui.msg("#{ui.color(" [#{connection.hostname}]", :cyan)} #{data}") end if r.exit_status != 0 ui.error("The following error occurred on #{server_name}:") @@ -602,24 +595,13 @@ class Chef ui.info("Connecting to #{ui.color(server_name, :bold)}") opts = connection_opts.dup do_connect(opts) - rescue => e - # Ugh. TODO: Train raises a Train::Transports::SSHFailed for a number of different errors. chef_core makes that - # a more general ConnectionFailed, with an error code based on the specific error text/reason provided from trainm. - # This means we have to look three layers into the exception to find out what actually happened instead of just - # looking at the exception type - # - # It doesn't help to provide our own error if it does't let the caller know what they need to identify the problem. - # Let's update chef_core to be a bit smarter about resolving the errors to an appropriate exception type - # (eg ChefCore::ConnectionFailed::AuthError or similar) that will work across protocols, instead of just a single - # ConnectionFailure type - # - - if e.cause && e.cause.cause && e.cause.cause.class == Net::SSH::AuthenticationFailed - if opts[:password] + rescue Train::Error => e + if e.cause && e.cause.class == Net::SSH::AuthenticationFailed + if connection.password_auth? raise else ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth") - password = ui.ask("Enter password for #{opts[:user]}@#{server_name} - trying password auth") do |q| + password = ui.ask("Enter password for #{opts[:user]}@#{server_name}.") do |q| q.echo = false end end @@ -630,6 +612,9 @@ class Chef end end + # TODO - maybe remove the footgun detection this was built on. + # url values override CLI flags, if you provide both + # we'll use the one that you gave in the URL. def connection_protocol return @connection_protocol if @connection_protocol from_url = host_descriptor =~ /^(.*):\/\// ? $1 : nil @@ -639,14 +624,8 @@ class Chef end def do_connect(conn_options) - # Resolve the given host name to a TargetHost instance. We will limit - # the number of hosts to 1 (effectivly eliminating wildcard support) since - # we only support running bootstrap against one host at a time. - resolver = ChefCore::TargetResolver.new(host_descriptor, connection_protocol, - conn_options, max_expanded_targets: 1) - @target_host = resolver.targets.first - target_host.connect! - target_host + @connection = TrainConnector.new(host_descriptor, connection_protocol, conn_options) + connection.connect! end # Fail if both first_boot_attributes and first_boot_attributes_from_file @@ -663,7 +642,7 @@ class Chef # TODO test for this method # TODO check that the protoocol is valid. def validate_winrm_transport_opts! - return true if connection_protocol != "winrm" + return true unless winrm? if Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])) if config_value(:winrm_auth_method) == "plaintext" && @@ -738,7 +717,7 @@ class Chef end def winrm_warn_no_ssl_verification - return if connection_protocol != "winrm" + return unless winrm? # REVIEWER NOTE # The original check from knife plugin did not include winrm_ssl_peer_fingerprint @@ -767,13 +746,8 @@ class Chef end end - # - - # Create a configuration hash for TargetHost to connect - # to the remote host via Train. - # # @return a configuration hash suitable for connecting to the remote - # host via TargetHost. + # host via train def connection_opts return @connection_opts unless @connection_opts.nil? @connection_opts = {} @@ -787,9 +761,16 @@ class Chef @connection_opts end + def winrm? + connection_protocol == "winrm" + end + + def ssh? + connection_protocol == "ssh" + end + # Common configuration for all protocols def base_opts - # port = config_value(:connection_port, knife_key_for_protocol(connection_protocol, :port)) user = config_value(:connection_user, @@ -806,10 +787,9 @@ class Chef end def host_verify_opts - case connection_protocol - when "winrm" + if winrm? { self_signed: config_value(:winrm_no_verify_cert) === true } - when "ssh" + elsif ssh? # Fall back to the old knife config key name for back compat. { verify_host_key: config_value(:ssh_verify_host_key, :host_key_verify, true) === true } @@ -958,16 +938,16 @@ class Chef end def upload_bootstrap(content) - script_name = target_host.base_os == :windows ? "bootstrap.bat" : "bootstrap.sh" - remote_path = target_host.normalize_path(File.join(target_host.temp_dir, script_name)) - target_host.save_as_remote_file(content, remote_path) + script_name = connection.windows? ? "bootstrap.bat" : "bootstrap.sh" + remote_path = connection.normalize_path(File.join(connection.temp_dir, script_name)) + connection.upload_file_content!(content, remote_path) remote_path end # build the command string for bootrapping # @return String def bootstrap_command(remote_path) - if target_host.base_os == :windows + if connection.windows? "cmd.exe /C #{remote_path}" else "sh #{remote_path}" @@ -976,8 +956,9 @@ class Chef # To avoid cluttering the CLI options, some flags (such as port and user) # are shared between protocols. However, there is still a need to allow the operator - # to specify defaults separately, since they may not be the same values for different protocols. - # + # to specify defaults separately, since they may not be the same values for different + # protocols. + # These keys are available in Chef::Config, and are prefixed with the protocol name. # For example, :user CLI option will map to :winrm_user and :ssh_user Chef::Config keys, # based on the connection protocol in use. diff --git a/lib/chef/knife/bootstrap/templates/chef-full.erb b/lib/chef/knife/bootstrap/templates/chef-full.erb index ec1f7e72c9..58a64a23b8 100644 --- a/lib/chef/knife/bootstrap/templates/chef-full.erb +++ b/lib/chef/knife/bootstrap/templates/chef-full.erb @@ -172,7 +172,7 @@ do_download() { <%= knife_config[:bootstrap_install_command] %> <% else %> install_sh="<%= knife_config[:bootstrap_url] ? knife_config[:bootstrap_url] : "https://omnitruck.chef.io/chef/install.sh" %>" - if test -f /usr/bin/chef-client; then + if test -f /usr/bin/<%= Chef::Dist::CLIENT %>}; then echo "-----> Existing <%= Chef::Dist::PRODUCT %> installation detected" else echo "-----> Installing Chef Omnibus (<%= latest_current_chef_version_string %>)" diff --git a/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb b/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb index 37fcf15682..fd7b48f28d 100644 --- a/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb +++ b/lib/chef/knife/bootstrap/templates/windows-chef-client-msi.erb @@ -110,7 +110,7 @@ goto chef_installed :chef_installed @echo Checking for existing chef installation -WHERE chef-client >nul 2>nul +WHERE <%= Chef::Dist::CLIENT %> >nul 2>nul If !ERRORLEVEL!==0 ( @echo Existing Chef installation detected, skipping download goto key_create @@ -120,14 +120,14 @@ If !ERRORLEVEL!==0 ( ) :install -@rem If user has provided the custom installation command for chef-client then execute it +@rem If user has provided the custom installation command for <%= Chef::Dist::CLIENT %> then execute it <% if @chef_config[:knife][:bootstrap_install_command] %> <%= @chef_config[:knife][:bootstrap_install_command] %> <% else %> - @rem Install Chef using chef-client MSI installer + @rem Install Chef using <%= Chef::Dist::CLIENT %> MSI installer @set "LOCAL_DESTINATION_MSI_PATH=<%= local_download_path %>" - @set "CHEF_CLIENT_MSI_LOG_PATH=%TEMP%\chef-client-msi%RANDOM%.log" + @set "CHEF_CLIENT_MSI_LOG_PATH=%TEMP%\<%= Chef::Dist::CLIENT %>-msi%RANDOM%.log" @rem Clear any pre-existing downloads @echo Checking for existing downloaded package at "%LOCAL_DESTINATION_MSI_PATH%" @@ -197,7 +197,7 @@ If !ERRORLEVEL!==0 ( <%= install_chef %> @if ERRORLEVEL 1 ( - echo Chef-client package failed to install with status code !ERRORLEVEL!. > "&2" + echo <%= Chef::Dist::CLIENT %> package failed to install with status code !ERRORLEVEL!. > "&2" echo See installation log for additional detail: %CHEF_CLIENT_MSI_LOG_PATH%. > "&2" ) else ( @echo Installation completed successfully diff --git a/lib/chef/knife/bootstrap/train_connector.rb b/lib/chef/knife/bootstrap/train_connector.rb new file mode 100644 index 0000000000..5230d6638c --- /dev/null +++ b/lib/chef/knife/bootstrap/train_connector.rb @@ -0,0 +1,286 @@ +# Copyright:: Copyright (c) 2019 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. +# + +require "train" +require "tempfile" +require "uri" + +class Chef + class Knife + class Bootstrap < Knife + class TrainConnector + SSH_CONFIG_OVERRIDE_KEYS = [:user, :port, :proxy].freeze + + MKTEMP_WIN_COMMAND = <<~EOM.freeze + $parent = [System.IO.Path]::GetTempPath(); + [string] $name = [System.Guid]::NewGuid(); + $tmp = New-Item -ItemType Directory -Path; + (Join-Path $parent $name); + $tmp.FullName + EOM + + MKTEMP_NIX_COMMAND = "bash -c 'd=$(mktemp -d ${TMPDIR:-/tmp}/chef_XXXXXX); echo $d'".freeze + + def initialize(host_url, default_transport, opts) + uri_opts = opts_from_uri(host_url) + uri_opts[:backend] ||= @default_transport + @transport_type = uri_opts[:backend] + + # opts in the URI will override user-provided options + @config = transport_config(host_url, opts.merge(uri_opts)) + end + + # Because creating a valid train connection for testing is a two-step process in which + # we need to connect before mocking config, + # we expose test_instance as a way for tests to create actual instances + # but ensure that they don't connect to any back end. + def self.test_instance(url, protocol: "ssh", + family: "unknown", name: "unknown", + release: "unknown", arch: "x86_64", + opts: {}) + # Specifying sudo: false ensures that attempted operations + # don't fail because the mock platform doesn't support sudo + tc = TrainConnector.new(url, protocol, { sudo: false }.merge(opts)) + tc.connect! + tc.connection.mock_os( + family: family, + name: name, + release: release, + arch: arch + ) + tc + end + + def connect! + # Force connection to establish + connection.wait_until_ready + true + end + + def hostname + @config[:host] + end + + def password_auth? + @config.key? :password + end + + # True if we're connected to a linux host + def linux? + connection.platform.linux? + end + + # True if we're connected to a unix host. + # NOTE: this is always true + # for a linux host because train classifies + # linux as a unix + def unix? + connection.platform.unix? + end + + # True if we're connected to a windows host + def windows? + connection.platform.windows? + end + + def winrm? + @transport_type == "winrm" + end + + def ssh? + @transport_type == "ssh" + end + + # Creates a temporary directory on the remote host if it + # hasn't already. Caches directory location. + # + # Returns the path on the remote host. + def temp_dir + cmd = windows? ? MKTEMP_WIN_COMMAND : MKTEMP_NIX_COMMAND + @tmpdir ||= begin + res = run_command!(cmd) + dir = res.stdout.chomp.strip + unless windows? + # Ensure that dir has the correct owner. We are possibly + # running with sudo right now - so this directory would be owned by root. + # File upload is performed over SCP as the current logged-in user, + # so we'll set ownership to ensure that works. + run_command!("chown #{@config[:user]} '#{dir}'") + end + dir + end + end + + def upload_file!(local_path, remote_path) + connection.upload(local_path, remote_path) + end + + def upload_file_content!(content, remote_path) + t = Tempfile.new("chef-content") + t << content + t.close + upload_file!(t.path, remote_path) + ensure + t.close + t.unlink + end + + def del_file!(path) + if windows? + run_command!("If (Test-Path \"#{path}\") { Remove-Item -Force -Path \"#{path}\" }") + else + run_command!("rm -f \"#{path}\"") + end + end + + # normalizes path across OS's + def normalize_path(path) + path.tr("\\", "/") + end + + def run_command(command, &data_handler) + connection.run_command(command, &data_handler) + end + + def run_command!(command, &data_handler) + result = run_command(command, &data_handler) + if result.exit_status != 0 + raise RemoteExecutionFailed.new(hostname, command, result) + end + result + end + + def connection + @connection ||= begin + Train.validate_backend(@config) + train = Train.create(@transport_type, @config) + train.connection + end + end + + private + + # For a given url and set of options, create a config + # hash suitable for passing into train. + def transport_config(host_url, opts_in) + opts = { target: host_url, + sudo: opts_in[:sudo] === false ? false : true, + www_form_encoded_password: true, + key_files: opts_in[:key_files], + non_interactive: true, # Prevent password prompts + transport_retries: 2, + transport_retry_sleep: 1, + logger: opts_in[:logger], + backend: @transport_type } + + # Base opts are those provided by the caller directly + opts.merge!(opts_from_caller(opts, opts_in)) + + # WinRM has some additional computed options + opts.merge!(opts_inferred_from_winrm(opts, opts_in)) + + # Now that everything is populated, fill in anything left + # from user ssh config that may be present + opts.merge!(missing_opts_from_ssh_config(opts, opts_in)) + + Train.target_config(opts) + end + + # Some winrm options are inferred based on other options. + # Return a hash of winrm options based on configuration already built. + def opts_inferred_from_winrm(config, opts_in) + return {} unless winrm? + opts_out = {} + + if opts_in[:ssl] + opts_out[:ssl] = true + opts_out[:self_signed] = opts_in[:self_signed] || false + end + + # See note here: https://github.com/mwrock/WinRM#example + if %w{ssl plaintext}.include?(opts_in[:winrm_auth_method]) + opts_out[:winrm_disable_sspi] = true + end + opts_out + end + + # Returns a hash containing valid options for the current + # transport protocol that are not already present in config + def opts_from_caller(config, opts_in) + # Train.options gives us the supported config options for the + # backend provider (ssh, winrm). We'll use that + # to filter out options that don't belong + # to the transport type we're using. + valid_opts = Train.options(config[:backend]) + opts_in.select do |key, _v| + valid_opts.key?(key) && !config.key?(key) + end + end + + # Extract any of username/password/host/port/transport + # that are in the URI and return them as a config has + def opts_from_uri(uri) + # Train.unpack_target_from_uri only works for complete URIs in + # form of proto://[user[:pass]@]host[:port]/ + # So we'll add the protocol prefix if it's not supplied. + uri_to_check = if URI.regexp.match(uri) + uri + else + "#{@transport_type}://#{uri}" + end + + Train.unpack_target_from_uri(uri_to_check) + end + + # This returns a hash that consists of settings + # populated from SSH configuration that are not already present + # in the configuration passed in. + # This is necessary because train will default these values + # itself - causing SSH config data to be ignored + def missing_opts_from_ssh_config(config, opts_in) + return {} unless ssh? + host_cfg = ssh_config_for_host(config[:host]) + opts_out = {} + opts_in.each do |key, _value| + if SSH_CONFIG_OVERRIDE_KEYS.include?(key) && !config.key?(key) + opts_out[key] = host_cfg[key] + end + end + opts_out + end + + # Having this as a method makes it easier to mock + # SSH Config for testing. + def ssh_config_for_host(host) + require "net/ssh" + Net::SSH::Config.for(host) + end + end + + class RemoteExecutionFailed < StandardError + attr_reader :exit_status, :command, :hostname, :stdout, :stderr + def initialize(hostname, command, result) + @hostname = hostname + @exit_status = result.exit_status + @stderr = result.stderr + @stdout = result.stdout + end + end + + end + end +end diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb index 355978c79d..5e987745e6 100644 --- a/lib/chef/knife/core/bootstrap_context.rb +++ b/lib/chef/knife/core/bootstrap_context.rb @@ -19,6 +19,7 @@ require "chef/run_list" require "chef/util/path_helper" require "pathname" +require "chef/dist" class Chef class Knife @@ -169,7 +170,7 @@ class Chef def start_chef # If the user doesn't have a client path configure, let bash use the PATH for what it was designed for - client_path = @chef_config[:chef_client_path] || "chef-client" + client_path = @chef_config[:chef_client_path] || "#{Chef::Dist::CLIENT}" s = "#{client_path} -j /etc/chef/first-boot.json" if @config[:verbosity] && @config[:verbosity] >= 3 s << " -l trace" diff --git a/lib/chef/knife/core/windows_bootstrap_context.rb b/lib/chef/knife/core/windows_bootstrap_context.rb index c7b8666ed0..df69399074 100644 --- a/lib/chef/knife/core/windows_bootstrap_context.rb +++ b/lib/chef/knife/core/windows_bootstrap_context.rb @@ -18,6 +18,7 @@ require "chef/knife/core/bootstrap_context" require "chef/util/path_helper" +require "chef/dist" class Chef class Knife @@ -268,7 +269,7 @@ class Chef end def local_download_path - "%TEMP%\\chef-client-latest.msi" + "%TEMP%\\#{Chef::Dist::CLIENT}-latest.msi" end def msi_url(machine_os = nil, machine_arch = nil, download_context = nil) diff --git a/lib/chef/log/syslog.rb b/lib/chef/log/syslog.rb index 58d6671095..d4179d465c 100644 --- a/lib/chef/log/syslog.rb +++ b/lib/chef/log/syslog.rb @@ -19,6 +19,7 @@ require "logger" require "syslog-logger" require "chef/mixin/unformatter" +require "chef/dist" class Chef class Log @@ -32,7 +33,7 @@ class Chef attr_accessor :sync, :formatter - def initialize(program_name = "chef-client", facility = ::Syslog::LOG_DAEMON, logopts = nil) + def initialize(program_name = "#{Chef::Dist::CLIENT}", facility = ::Syslog::LOG_DAEMON, logopts = nil) super return if defined? ::Logger::Syslog::SYSLOG ::Logger::Syslog.const_set :SYSLOG, SYSLOG diff --git a/lib/chef/policy_builder/policyfile.rb b/lib/chef/policy_builder/policyfile.rb index a4134c02bd..421d2ca8ec 100644 --- a/lib/chef/policy_builder/policyfile.rb +++ b/lib/chef/policy_builder/policyfile.rb @@ -24,6 +24,7 @@ require "chef/run_context" require "chef/config" require "chef/node" require "chef/server_api" +require "chef/dist" class Chef module PolicyBuilder @@ -90,7 +91,7 @@ class Chef @node = nil if Chef::Config[:solo_legacy_mode] - raise UnsupportedFeature, "Policyfile does not support chef-solo. Use chef-client local mode instead." + raise UnsupportedFeature, "Policyfile does not support chef-solo. Use #{Chef::Dist::CLIENT} local mode instead." end if override_runlist diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb index 05522f1eb8..589a70de09 100644 --- a/lib/chef/provider/file.rb +++ b/lib/chef/provider/file.rb @@ -31,6 +31,7 @@ require "chef/util/backup" require "chef/util/diff" require "chef/util/selinux" require "chef/file_content_management/deploy" +require "chef/dist" # The Tao of File Providers: # - the content provider must always return a tempfile that we can delete/mv @@ -389,7 +390,7 @@ class Chef return if tempfile.nil? # but a tempfile that has no path or doesn't exist should not happen if tempfile.path.nil? || !::File.exists?(tempfile.path) - raise "chef-client is confused, trying to deploy a file that has no path or does not exist..." + raise "#{Chef::Dist::CLIENT} is confused, trying to deploy a file that has no path or does not exist..." end # the file? on the next line suppresses the case in why-run when we have a not-file here that would have otherwise been removed diff --git a/lib/chef/provider/windows_task.rb b/lib/chef/provider/windows_task.rb index ab88575510..34c64199bf 100644 --- a/lib/chef/provider/windows_task.rb +++ b/lib/chef/provider/windows_task.rb @@ -334,7 +334,8 @@ class Chef task.parameters != new_resource.command_arguments.to_s || task.principals[:run_level] != run_level || task.settings[:disallow_start_if_on_batteries] != new_resource.disallow_start_if_on_batteries || - task.settings[:stop_if_going_on_batteries] != new_resource.stop_if_going_on_batteries) + task.settings[:stop_if_going_on_batteries] != new_resource.stop_if_going_on_batteries || + task.settings[:start_when_available] != new_resource.start_when_available) else current_task_trigger = task.trigger(0) new_task_trigger = trigger @@ -360,7 +361,8 @@ class Chef task.principals[:run_level] != run_level || PRIORITY[task.priority] != new_resource.priority || task.settings[:disallow_start_if_on_batteries] != new_resource.disallow_start_if_on_batteries || - task.settings[:stop_if_going_on_batteries] != new_resource.stop_if_going_on_batteries + task.settings[:stop_if_going_on_batteries] != new_resource.stop_if_going_on_batteries || + task.settings[:start_when_available] != new_resource.start_when_available if trigger_type == TaskScheduler::MONTHLYDATE flag = true if current_task_trigger[:run_on_last_day_of_month] != new_task_trigger[:run_on_last_day_of_month] end @@ -560,12 +562,13 @@ class Chef settings[:priority] = new_resource.priority settings[:disallow_start_if_on_batteries] = new_resource.disallow_start_if_on_batteries settings[:stop_if_going_on_batteries] = new_resource.stop_if_going_on_batteries + settings[:start_when_available] = new_resource.start_when_available settings end def principal_settings settings = {} - settings [:run_level] = run_level + settings[:run_level] = run_level settings[:logon_type] = logon_type settings end diff --git a/lib/chef/resource/breakpoint.rb b/lib/chef/resource/breakpoint.rb index 8af3edbe22..820ac70c09 100644 --- a/lib/chef/resource/breakpoint.rb +++ b/lib/chef/resource/breakpoint.rb @@ -17,6 +17,7 @@ # require "chef/resource" +require "chef/dist" class Chef class Resource @@ -24,7 +25,7 @@ class Chef provides :breakpoint resource_name :breakpoint - description "Use the breakpoint resource to add breakpoints to recipes. Run the chef-shell in chef-client mode, and then use those breakpoints to debug recipes. Breakpoints are ignored by the chef-client during an actual chef-client run. That said, breakpoints are typically used to debug recipes only when running them in a non-production environment, after which they are removed from those recipes before the parent cookbook is uploaded to the Chef server." + description "Use the breakpoint resource to add breakpoints to recipes. Run the chef-shell in #{Chef::Dist::CLIENT} mode, and then use those breakpoints to debug recipes. Breakpoints are ignored by the #{Chef::Dist::CLIENT} during an actual #{Chef::Dist::CLIENT} run. That said, breakpoints are typically used to debug recipes only when running them in a non-production environment, after which they are removed from those recipes before the parent cookbook is uploaded to the Chef server." introduced "12.0" default_action :break diff --git a/lib/chef/resource/chef_gem.rb b/lib/chef/resource/chef_gem.rb index 2079e5d796..7655d6651c 100644 --- a/lib/chef/resource/chef_gem.rb +++ b/lib/chef/resource/chef_gem.rb @@ -18,6 +18,7 @@ require "chef/resource/package" require "chef/resource/gem_package" +require "chef/dist" class Chef class Resource @@ -37,12 +38,12 @@ class Chef resource_name :chef_gem property :gem_binary, default: "#{RbConfig::CONFIG['bindir']}/gem", default_description: "Chef's built-in gem binary.", - description: "The path of a gem binary to use for the installation. By default, the same version of Ruby that is used by the chef-client will be installed.", + description: "The path of a gem binary to use for the installation. By default, the same version of Ruby that is used by the #{Chef::Dist::CLIENT} will be installed.", callbacks: { "The chef_gem resource is restricted to the current gem environment, use gem_package to install to other environments." => proc { |v| v == "#{RbConfig::CONFIG['bindir']}/gem" }, } property :compile_time, [TrueClass, FalseClass], - description: "Controls the phase during which a gem is installed on a node. Set to 'true' to install a gem while the resource collection is being built (the 'compile phase'). Set to 'false' to install a gem while the chef-client is configuring the node (the 'converge phase').", + description: "Controls the phase during which a gem is installed on a node. Set to 'true' to install a gem while the resource collection is being built (the 'compile phase'). Set to 'false' to install a gem while the #{Chef::Dist::CLIENT} is configuring the node (the 'converge phase').", default: false, desired_state: false # force the resource to compile time if the compile time property has been set diff --git a/lib/chef/resource/cookbook_file.rb b/lib/chef/resource/cookbook_file.rb index c96e794469..bee4a5f3f4 100644 --- a/lib/chef/resource/cookbook_file.rb +++ b/lib/chef/resource/cookbook_file.rb @@ -21,6 +21,7 @@ require "chef/resource/file" require "chef/provider/cookbook_file" require "chef/mixin/securable" +require "chef/dist" class Chef class Resource @@ -29,7 +30,7 @@ class Chef resource_name :cookbook_file - description "Use the cookbook_file resource to transfer files from a sub-directory of COOKBOOK_NAME/files/ to a specified path located on a host that is running the chef-client. The file is selected according to file specificity, which allows different source files to be used based on the hostname, host platform (operating system, distro, or as appropriate), or platform version. Files that are located in the COOKBOOK_NAME/files/default sub-directory may be used on any platform.\n\nDuring a chef-client run, the checksum for each local file is calculated and then compared against the checksum for the same file as it currently exists in the cookbook on the Chef server. A file is not transferred when the checksums match. Only files that require an update are transferred from the Chef server to a node." + description "Use the cookbook_file resource to transfer files from a sub-directory of COOKBOOK_NAME/files/ to a specified path located on a host that is running the #{Chef::Dist::CLIENT}. The file is selected according to file specificity, which allows different source files to be used based on the hostname, host platform (operating system, distro, or as appropriate), or platform version. Files that are located in the COOKBOOK_NAME/files/default sub-directory may be used on any platform.\n\nDuring a #{Chef::Dist::CLIENT} run, the checksum for each local file is calculated and then compared against the checksum for the same file as it currently exists in the cookbook on the Chef server. A file is not transferred when the checksums match. Only files that require an update are transferred from the Chef server to a node." property :source, [ String, Array ], description: "The name of the file in COOKBOOK_NAME/files/default or the path to a file located in COOKBOOK_NAME/files. The path must include the file name and its extension. This can be used to distribute specific files depending upon the platform used.", diff --git a/lib/chef/resource/dnf_package.rb b/lib/chef/resource/dnf_package.rb index 3b40a6dffb..6590c6c003 100644 --- a/lib/chef/resource/dnf_package.rb +++ b/lib/chef/resource/dnf_package.rb @@ -18,6 +18,7 @@ require "chef/resource/package" require "chef/mixin/which" require "chef/mixin/shell_out" +require "chef/dist" class Chef class Resource @@ -52,7 +53,7 @@ class Chef # Flush the in-memory available/installed cache, this does not flush the dnf caches on disk property :flush_cache, Hash, - description: "Flush the in-memory cache before or after a DNF operation that installs, upgrades, or removes a package. DNF automatically synchronizes remote metadata to a local cache. The chef-client creates a copy of the local cache, and then stores it in-memory during the chef-client run. The in-memory cache allows packages to be installed during the chef-client run without the need to continue synchronizing the remote metadata to the local cache while the chef-client run is in-progress.", + description: "Flush the in-memory cache before or after a DNF operation that installs, upgrades, or removes a package. DNF automatically synchronizes remote metadata to a local cache. The #{Chef::Dist::CLIENT} creates a copy of the local cache, and then stores it in-memory during the #{Chef::Dist::CLIENT} run. The in-memory cache allows packages to be installed during the #{Chef::Dist::CLIENT} run without the need to continue synchronizing the remote metadata to the local cache while the #{Chef::Dist::CLIENT} run is in-progress.", default: { before: false, after: false }, coerce: proc { |v| if v.is_a?(Hash) diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb index afab9a7b27..de3b7e044a 100644 --- a/lib/chef/resource/execute.rb +++ b/lib/chef/resource/execute.rb @@ -18,6 +18,7 @@ # require "chef/resource" +require "chef/dist" class Chef class Resource @@ -65,7 +66,7 @@ class Chef description: "The group name or group ID that must be changed before running a command." property :live_stream, [ TrueClass, FalseClass ], default: false, - description: "Send the output of the command run by this execute resource block to the chef-client event stream." + description: "Send the output of the command run by this execute resource block to the #{Chef::Dist::CLIENT} event stream." # default_env defaults to `false` so that the command execution more exactly matches what the user gets on the command line without magic property :default_env, [ TrueClass, FalseClass ], desired_state: false, default: false, @@ -91,7 +92,7 @@ class Chef # lazy used to set default value of sensitive to true if password is set property :sensitive, [ TrueClass, FalseClass ], - description: "Ensure that sensitive resource data is not logged by the chef-client.", + description: "Ensure that sensitive resource data is not logged by the #{Chef::Dist::CLIENT}.", default: lazy { |r| r.password ? true : false }, default_description: "True if the password property is set. False otherwise." property :elevated, [ TrueClass, FalseClass ], default: false, diff --git a/lib/chef/resource/file.rb b/lib/chef/resource/file.rb index 8db15fc661..1875846d79 100644 --- a/lib/chef/resource/file.rb +++ b/lib/chef/resource/file.rb @@ -22,6 +22,7 @@ require "chef/platform/query_helpers" require "chef/mixin/securable" require "chef/resource/file/verification" require "pathname" +require "chef/dist" class Chef class Resource @@ -52,7 +53,7 @@ class Chef allowed_actions :create, :delete, :touch, :create_if_missing property :path, String, name_property: true, identity: true, - description: "The full path to the file, including the file name and its extension. For example: /files/file.txt. Default value: the name of the resource block. Microsoft Windows: A path that begins with a forward slash (/) will point to the root of the current working directory of the chef-client process. This path can vary from system to system. Therefore, using a path that begins with a forward slash (/) is not recommended." + description: "The full path to the file, including the file name and its extension. For example: /files/file.txt. Default value: the name of the resource block. Microsoft Windows: A path that begins with a forward slash (/) will point to the root of the current working directory of the #{Chef::Dist::CLIENT} process. This path can vary from system to system. Therefore, using a path that begins with a forward slash (/) is not recommended." property :atomic_update, [ TrueClass, FalseClass ], desired_state: false, default: lazy { |r| r.docker? && r.special_docker_files?(r.path) ? false : Chef::Config[:file_atomic_update] }, description: "Perform atomic file updates on a per-resource basis. Set to true for atomic file updates. Set to false for non-atomic file updates. This setting overrides file_atomic_update, which is a global setting found in the client.rb file." @@ -69,7 +70,7 @@ class Chef property :diff, [ String, nil ], desired_state: false, skip_docs: true property :force_unlink, [ TrueClass, FalseClass ], desired_state: false, default: false, - description: "How the chef-client handles certain situations when the target file turns out not to be a file. For example, when a target file is actually a symlink. Set to true for the chef-client delete the non-file target and replace it with the specified file. Set to false for the chef-client to raise an error." + description: "How the #{Chef::Dist::CLIENT} handles certain situations when the target file turns out not to be a file. For example, when a target file is actually a symlink. Set to true for the #{Chef::Dist::CLIENT} delete the non-file target and replace it with the specified file. Set to false for the #{Chef::Dist::CLIENT} to raise an error." property :manage_symlink_source, [ TrueClass, FalseClass ], desired_state: false, description: "Change the behavior of the file resource if it is pointed at a symlink. When this value is set to true, the Chef client will manage the symlink's permissions or will replace the symlink with a normal file if the resource has content. When this value is set to false, Chef will follow the symlink and will manage the permissions and content of symlink's target file. The default behavior is true but emits a warning that the default value will be changed to false in a future version; setting this explicitly to true or false suppresses this warning." diff --git a/lib/chef/resource/gem_package.rb b/lib/chef/resource/gem_package.rb index 0f6fd413ca..e057cbd485 100644 --- a/lib/chef/resource/gem_package.rb +++ b/lib/chef/resource/gem_package.rb @@ -17,6 +17,7 @@ # require "chef/resource/package" +require "chef/dist" class Chef class Resource @@ -44,7 +45,7 @@ class Chef default: lazy { Chef::Config[:clear_gem_sources] }, desired_state: false property :gem_binary, String, desired_state: false, - description: "The path of a gem binary to use for the installation. By default, the same version of Ruby that is used by the chef-client will be installed." + description: "The path of a gem binary to use for the installation. By default, the same version of Ruby that is used by the #{Chef::Dist::CLIENT} will be installed." property :include_default_source, [ TrueClass, FalseClass ], description: "Set to 'false' to not include 'Chef::Config[:rubygems_url]'' in the sources.", diff --git a/lib/chef/resource/homebrew_package.rb b/lib/chef/resource/homebrew_package.rb index 9d8c7b2596..930e25d820 100644 --- a/lib/chef/resource/homebrew_package.rb +++ b/lib/chef/resource/homebrew_package.rb @@ -20,6 +20,7 @@ require "chef/provider/package" require "chef/resource/package" +require "chef/dist" class Chef class Resource @@ -31,7 +32,7 @@ class Chef introduced "12.0" property :homebrew_user, [ String, Integer ], - description: "The name of the Homebrew owner to be used by the chef-client when executing a command." + description: "The name of the Homebrew owner to be used by the #{Chef::Dist::CLIENT} when executing a command." end end diff --git a/lib/chef/resource/ohai.rb b/lib/chef/resource/ohai.rb index b5cd97cae0..59f2967a2f 100644 --- a/lib/chef/resource/ohai.rb +++ b/lib/chef/resource/ohai.rb @@ -18,6 +18,7 @@ # require "chef/resource" +require "chef/dist" class Chef class Resource @@ -25,10 +26,10 @@ class Chef resource_name :ohai provides :ohai - description "Use the ohai resource to reload the Ohai configuration on a node. This allows recipes that change system attributes (like a recipe that adds a user) to refer to those attributes later on during the chef-client run." + description "Use the ohai resource to reload the Ohai configuration on a node. This allows recipes that change system attributes (like a recipe that adds a user) to refer to those attributes later on during the #{Chef::Dist::CLIENT} run." property :plugin, String, - description: "The name of an Ohai plugin to be reloaded. If this property is not specified, the chef-client will reload all plugins." + description: "The name of an Ohai plugin to be reloaded. If this property is not specified, the #{Chef::Dist::CLIENT} will reload all plugins." default_action :reload allowed_actions :reload diff --git a/lib/chef/resource/ruby_block.rb b/lib/chef/resource/ruby_block.rb index 911e6203d8..0ec6647fd8 100644 --- a/lib/chef/resource/ruby_block.rb +++ b/lib/chef/resource/ruby_block.rb @@ -19,11 +19,12 @@ require "chef/resource" require "chef/provider/ruby_block" +require "chef/dist" class Chef class Resource class RubyBlock < Chef::Resource - description "Use the ruby_block resource to execute Ruby code during a chef-client run."\ + description "Use the ruby_block resource to execute Ruby code during a #{Chef::Dist::CLIENT} run."\ " Ruby code in the ruby_block resource is evaluated with other resources during"\ " convergence, whereas Ruby code outside of a ruby_block resource is evaluated"\ " before other resources, as the recipe is compiled." diff --git a/lib/chef/resource/service.rb b/lib/chef/resource/service.rb index 4e808d6035..20bf9ecc65 100644 --- a/lib/chef/resource/service.rb +++ b/lib/chef/resource/service.rb @@ -19,6 +19,7 @@ require "chef/resource" require "shellwords" +require "chef/dist" class Chef class Resource @@ -33,7 +34,7 @@ class Chef # this is a poor API please do not re-use this pattern property :supports, Hash, default: { restart: nil, reload: nil, status: nil }, - description: "A list of properties that controls how the chef-client is to attempt to manage a service: :restart, :reload, :status. For :restart, the init script or other service provider can use a restart command; if :restart is not specified, the chef-client attempts to stop and then start a service. For :reload, the init script or other service provider can use a reload command. For :status, the init script or other service provider can use a status command to determine if the service is running; if :status is not specified, the chef-client attempts to match the service_name against the process table as a regular expression, unless a pattern is specified as a parameter property. Default value: { restart: false, reload: false, status: false } for all platforms (except for the Red Hat platform family, which defaults to { restart: false, reload: false, status: true }.)", + description: "A list of properties that controls how the #{Chef::Dist::CLIENT} is to attempt to manage a service: :restart, :reload, :status. For :restart, the init script or other service provider can use a restart command; if :restart is not specified, the #{Chef::Dist::CLIENT} attempts to stop and then start a service. For :reload, the init script or other service provider can use a reload command. For :status, the init script or other service provider can use a status command to determine if the service is running; if :status is not specified, the #{Chef::Dist::CLIENT} attempts to match the service_name against the process table as a regular expression, unless a pattern is specified as a parameter property. Default value: { restart: false, reload: false, status: false } for all platforms (except for the Red Hat platform family, which defaults to { restart: false, reload: false, status: true }.)", coerce: proc { |x| x.is_a?(Array) ? x.each_with_object({}) { |i, m| m[i] = true } : x } property :service_name, String, @@ -76,7 +77,7 @@ class Chef # specify overrides for the start_command, stop_command and # restart_command properties. property :init_command, String, - description: "The path to the init script that is associated with the service. Use init_command to prevent the need to specify overrides for the start_command, stop_command, and restart_command properties. When this property is not specified, the chef-client will use the default init command for the service provider being used.", + description: "The path to the init script that is associated with the service. Use init_command to prevent the need to specify overrides for the start_command, stop_command, and restart_command properties. When this property is not specified, the #{Chef::Dist::CLIENT} will use the default init command for the service provider being used.", desired_state: false # if the service is enabled or not diff --git a/lib/chef/resource/snap_package.rb b/lib/chef/resource/snap_package.rb index 81904f8405..080b604d28 100644 --- a/lib/chef/resource/snap_package.rb +++ b/lib/chef/resource/snap_package.rb @@ -24,6 +24,7 @@ class Chef resource_name :snap_package description "Use the snap_package resource to manage snap packages on Debian and Ubuntu platforms." + introduced "15.0" property :channel, String, description: "The default channel. For example: stable.", diff --git a/lib/chef/resource/subversion.rb b/lib/chef/resource/subversion.rb index adf8244668..4febb9353c 100644 --- a/lib/chef/resource/subversion.rb +++ b/lib/chef/resource/subversion.rb @@ -18,6 +18,7 @@ # require "chef/resource/scm" +require "chef/dist" class Chef class Resource @@ -32,7 +33,7 @@ class Chef default: "--no-auth-cache" property :svn_info_args, [String, nil, FalseClass], - description: "Use when the svn info command is used by the chef-client and arguments need to be passed. The svn_arguments command does not work when the svn info command is used.", + description: "Use when the svn info command is used by the #{Chef::Dist::CLIENT} and arguments need to be passed. The svn_arguments command does not work when the svn info command is used.", coerce: proc { |v| v == false ? nil : v }, # coerce false to nil default: "--no-auth-cache" diff --git a/lib/chef/resource/template.rb b/lib/chef/resource/template.rb index bd8325f2c1..b08bf0b8fb 100644 --- a/lib/chef/resource/template.rb +++ b/lib/chef/resource/template.rb @@ -20,6 +20,7 @@ require "chef/resource/file" require "chef/mixin/securable" +require "chef/dist" class Chef class Resource @@ -67,7 +68,7 @@ class Chef property :local, [ TrueClass, FalseClass ], default: false, desired_state: false, - description: "Load a template from a local path. By default, the chef-client loads templates from a cookbook’s /templates directory. When this property is set to true, use the source property to specify the path to a template on the local node." + description: "Load a template from a local path. By default, the #{Chef::Dist::CLIENT} loads templates from a cookbook’s /templates directory. When this property is set to true, use the source property to specify the path to a template on the local node." # Declares a helper method to be defined in the template context when # rendering. diff --git a/lib/chef/resource/windows_certificate.rb b/lib/chef/resource/windows_certificate.rb index e7261adfba..c530d3b67d 100644 --- a/lib/chef/resource/windows_certificate.rb +++ b/lib/chef/resource/windows_certificate.rb @@ -21,6 +21,7 @@ require "chef/util/path_helper" require "chef/resource" require "win32-certstore" if Chef::Platform.windows? require "openssl" +require "chef/dist" class Chef class Resource @@ -53,7 +54,7 @@ class Chef # lazy used to set default value of sensitive to true if password is set property :sensitive, [TrueClass, FalseClass], - description: "Ensure that sensitive resource data is not logged by the chef-client.", + description: "Ensure that sensitive resource data is not logged by the #{Chef::Dist::CLIENT}.", default: lazy { |r| r.pfx_password ? true : false }, skip_docs: true action :create do diff --git a/lib/chef/resource/windows_task.rb b/lib/chef/resource/windows_task.rb index 54920f2bac..c60515a0ee 100644 --- a/lib/chef/resource/windows_task.rb +++ b/lib/chef/resource/windows_task.rb @@ -120,6 +120,10 @@ class Chef introduced: "14.7", description: "The task description." + property :start_when_available, [TrueClass, FalseClass], + introduced: "15.0", default: false, + description: "To start the task at any time after its scheduled time has passed." + attr_accessor :exists, :task, :command_arguments VALID_WEEK_DAYS = %w{ mon tue wed thu fri sat sun * }.freeze diff --git a/lib/chef/resource/yum_package.rb b/lib/chef/resource/yum_package.rb index 602fe489fb..8f088371ed 100644 --- a/lib/chef/resource/yum_package.rb +++ b/lib/chef/resource/yum_package.rb @@ -17,6 +17,7 @@ # require "chef/resource/package" +require "chef/dist" class Chef class Resource @@ -51,7 +52,7 @@ class Chef coerce: proc { |x| x.is_a?(Array) ? x.to_a : x } property :flush_cache, Hash, - description: "Flush the in-memory cache before or after a Yum operation that installs, upgrades, or removes a package. Accepts a Hash in the form: { :before => true/false, :after => true/false } or an Array in the form [ :before, :after ].\nYum automatically synchronizes remote metadata to a local cache. The chef-client creates a copy of the local cache, and then stores it in-memory during the chef-client run. The in-memory cache allows packages to be installed during the chef-client run without the need to continue synchronizing the remote metadata to the local cache while the chef-client run is in-progress.", + description: "Flush the in-memory cache before or after a Yum operation that installs, upgrades, or removes a package. Accepts a Hash in the form: { :before => true/false, :after => true/false } or an Array in the form [ :before, :after ].\nYum automatically synchronizes remote metadata to a local cache. The #{Chef::Dist::CLIENT} creates a copy of the local cache, and then stores it in-memory during the #{Chef::Dist::CLIENT} run. The in-memory cache allows packages to be installed during the #{Chef::Dist::CLIENT} run without the need to continue synchronizing the remote metadata to the local cache while the #{Chef::Dist::CLIENT} run is in-progress.", default: { before: false, after: false }, coerce: proc { |v| if v.is_a?(Hash) diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 23584f4be2..14ae1e880a 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -23,7 +23,7 @@ require "chef/version_string" class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = Chef::VersionString.new("15.0.233") + VERSION = Chef::VersionString.new("15.0.237") end # diff --git a/lib/chef/win32/api/security.rb b/lib/chef/win32/api/security.rb index 277e85a26b..5c3dd69c3e 100644 --- a/lib/chef/win32/api/security.rb +++ b/lib/chef/win32/api/security.rb @@ -140,6 +140,8 @@ class Chef FILE_READ_EA | SYNCHRONIZE FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE + WRITE = FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA + SUBFOLDERS_AND_FILES_ONLY = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE # Access Token Rights (for OpenProcessToken) # Access Rights for Access-Token Objects (used in OpenProcessToken) TOKEN_ASSIGN_PRIMARY = 0x0001 diff --git a/spec/functional/resource/link_spec.rb b/spec/functional/resource/link_spec.rb index 4464b6ed69..d86a904098 100644 --- a/spec/functional/resource/link_spec.rb +++ b/spec/functional/resource/link_spec.rb @@ -417,11 +417,11 @@ describe Chef::Resource::Link do it_behaves_like "a securable resource without existing target" do let(:path) { target_file } - def allowed_acl(sid, expected_perms) + def allowed_acl(sid, expected_perms, _flags = 0) [ ACE.access_allowed(sid, expected_perms[:specific]) ] end - def denied_acl(sid, expected_perms) + def denied_acl(sid, expected_perms, _flags = 0) [ ACE.access_denied(sid, expected_perms[:specific]) ] end diff --git a/spec/functional/resource/windows_task_spec.rb b/spec/functional/resource/windows_task_spec.rb index b0c6998d77..fa51ad3f8a 100644 --- a/spec/functional/resource/windows_task_spec.rb +++ b/spec/functional/resource/windows_task_spec.rb @@ -18,6 +18,7 @@ require "spec_helper" require "chef/provider/windows_task" +require "chef/dist" describe Chef::Resource::WindowsTask, :windows_only do # resource.task.application_name will default to task_name unless resource.command is set @@ -45,37 +46,37 @@ describe Chef::Resource::WindowsTask, :windows_only do context "With Arguments" do it "creates scheduled task and sets command arguments" do - subject.command "chef-client -W" + subject.command "#{Chef::Dist::CLIENT} -W" call_for_create_action # loading current resource again to check new task is creted and it matches task parameters current_resource = call_for_load_current_resource expect(current_resource.exists).to eq(true) - expect(current_resource.task.application_name).to eq("chef-client") + expect(current_resource.task.application_name).to eq(Chef::Dist::CLIENT) expect(current_resource.task.parameters).to eq("-W") end it "does not converge the resource if it is already converged" do - subject.command "chef-client -W" + subject.command "#{Chef::Dist::CLIENT} -W" subject.run_action(:create) - subject.command "chef-client -W" + subject.command "#{Chef::Dist::CLIENT} -W" subject.run_action(:create) expect(subject).not_to be_updated_by_last_action end it "creates scheduled task and sets command arguments when arguments inclusive single quotes" do - subject.command "chef-client -W -L 'C:\\chef\\chef-ad-join.log'" + subject.command "#{Chef::Dist::CLIENT} -W -L 'C:\\chef\\chef-ad-join.log'" call_for_create_action # loading current resource again to check new task is creted and it matches task parameters current_resource = call_for_load_current_resource expect(current_resource.exists).to eq(true) - expect(current_resource.task.application_name).to eq("chef-client") + expect(current_resource.task.application_name).to eq(Chef::Dist::CLIENT) expect(current_resource.task.parameters).to eq("-W -L 'C:\\chef\\chef-ad-join.log'") end it "does not converge the resource if it is already converged" do - subject.command "chef-client -W -L 'C:\\chef\\chef-ad-join.log'" + subject.command "#{Chef::Dist::CLIENT} -W -L 'C:\\chef\\chef-ad-join.log'" subject.run_action(:create) - subject.command "chef-client -W -L 'C:\\chef\\chef-ad-join.log'" + subject.command "#{Chef::Dist::CLIENT} -W -L 'C:\\chef\\chef-ad-join.log'" subject.run_action(:create) expect(subject).not_to be_updated_by_last_action end @@ -135,19 +136,19 @@ describe Chef::Resource::WindowsTask, :windows_only do context "Without Arguments" do it "creates scheduled task and sets command arguments" do - subject.command "chef-client" + subject.command Chef::Dist::CLIENT call_for_create_action # loading current resource again to check new task is creted and it matches task parameters current_resource = call_for_load_current_resource expect(current_resource.exists).to eq(true) - expect(current_resource.task.application_name).to eq("chef-client") + expect(current_resource.task.application_name).to eq(Chef::Dist::CLIENT) expect(current_resource.task.parameters).to be_empty end it "does not converge the resource if it is already converged" do - subject.command "chef-client" + subject.command Chef::Dist::CLIENT subject.run_action(:create) - subject.command "chef-client" + subject.command Chef::Dist::CLIENT subject.run_action(:create) expect(subject).not_to be_updated_by_last_action end @@ -1283,6 +1284,57 @@ describe Chef::Resource::WindowsTask, :windows_only do expect(subject).not_to be_updated_by_last_action end end + + context "when start_when_available is passed" do + subject do + new_resource = Chef::Resource::WindowsTask.new(task_name, run_context) + new_resource.command task_name + new_resource.run_level :highest + new_resource.execution_time_limit = 259200 / 60 # converting "PT72H" into minutes and passing here since win32-taskscheduler accespts this + new_resource + end + + it "sets start_when_available to true" do + subject.frequency :minute + subject.start_when_available true + call_for_create_action + # loading current resource again to check new task is creted and it matches task parameters + current_resource = call_for_load_current_resource + expect(current_resource.exists).to eq(true) + expect(current_resource.task.settings[:start_when_available]).to eql(true) + end + + it "sets start_when_available to false" do + subject.frequency :minute + subject.start_when_available false + call_for_create_action + # loading current resource again to check new task is created and it matches task parameters + current_resource = call_for_load_current_resource + expect(current_resource.exists).to eq(true) + expect(current_resource.task.settings[:start_when_available]).to eql(false) + end + + it "sets the default if start_when_available is nil" do + subject.frequency :minute + subject.start_when_available nil + call_for_create_action + # loading current resource again to check new task is created and it matches task parameters + current_resource = call_for_load_current_resource + expect(current_resource.exists).to eq(true) + expect(current_resource.task.settings[:start_when_available]).to eql(false) + end + + it "does not converge the resource if it is already converged" do + subject.frequency :minute + subject.start_when_available true + subject.run_action(:create) + subject.frequency :minute + subject.start_when_available true + subject.disallow_start_if_on_batteries false + subject.run_action(:create) + expect(subject).not_to be_updated_by_last_action + end + end end context "task_name with parent folder" do diff --git a/spec/support/shared/functional/directory_resource.rb b/spec/support/shared/functional/directory_resource.rb index 5e5e2bb360..4fb08479e6 100644 --- a/spec/support/shared/functional/directory_resource.rb +++ b/spec/support/shared/functional/directory_resource.rb @@ -65,18 +65,20 @@ shared_examples_for "a directory resource" do end # Set up the context for security tests - def allowed_acl(sid, expected_perms) - [ - ACE.access_allowed(sid, expected_perms[:specific]), - ACE.access_allowed(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::INHERIT_ONLY_ACE | Chef::ReservedNames::Win32::API::Security::CONTAINER_INHERIT_ACE | Chef::ReservedNames::Win32::API::Security::OBJECT_INHERIT_ACE)), - ] + def allowed_acl(sid, expected_perms, flags = 0) + acl = [ ACE.access_allowed(sid, expected_perms[:specific], flags) ] + if expected_perms[:generic] + acl << ACE.access_allowed(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::SUBFOLDERS_AND_FILES_ONLY)) + end + acl end - def denied_acl(sid, expected_perms) - [ - ACE.access_denied(sid, expected_perms[:specific]), - ACE.access_denied(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::INHERIT_ONLY_ACE | Chef::ReservedNames::Win32::API::Security::CONTAINER_INHERIT_ACE | Chef::ReservedNames::Win32::API::Security::OBJECT_INHERIT_ACE)), - ] + def denied_acl(sid, expected_perms, flags = 0) + acl = [ ACE.access_denied(sid, expected_perms[:specific], flags) ] + if expected_perms[:generic] + acl << ACE.access_denied(sid, expected_perms[:generic], (Chef::ReservedNames::Win32::API::Security::SUBFOLDERS_AND_FILES_ONLY)) + end + acl end def parent_inheritable_acls diff --git a/spec/support/shared/functional/file_resource.rb b/spec/support/shared/functional/file_resource.rb index 8ae5db6a57..db947614b3 100644 --- a/spec/support/shared/functional/file_resource.rb +++ b/spec/support/shared/functional/file_resource.rb @@ -899,11 +899,11 @@ shared_examples_for "a configured file resource" do end # Set up the context for security tests - def allowed_acl(sid, expected_perms) + def allowed_acl(sid, expected_perms, _flags = 0) [ ACE.access_allowed(sid, expected_perms[:specific]) ] end - def denied_acl(sid, expected_perms) + def denied_acl(sid, expected_perms, _flags = 0) [ ACE.access_denied(sid, expected_perms[:specific]) ] end diff --git a/spec/support/shared/functional/securable_resource.rb b/spec/support/shared/functional/securable_resource.rb index 2abae030c2..18e7243453 100644 --- a/spec/support/shared/functional/securable_resource.rb +++ b/spec/support/shared/functional/securable_resource.rb @@ -117,8 +117,7 @@ shared_context "use Windows permissions", :windows_only do let(:expected_write_perms) do { - generic: Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE, - specific: Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE, + specific: Chef::ReservedNames::Win32::API::Security::WRITE, } end @@ -136,6 +135,8 @@ shared_context "use Windows permissions", :windows_only do } end + let (:write_flag) { 3 } + RSpec::Matchers.define :have_expected_properties do |mask, type, flags| match do |ace| ace.mask == mask && @@ -363,78 +364,108 @@ shared_examples_for "a securable resource without existing target" do expect(descriptor.group).to eq(arbitrary_non_default_group) end - describe "with rights and deny_rights attributes" do - - it "correctly sets :read rights" do - resource.rights(:read, "Guest") - resource.run_action(:create) - expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_perms)) + describe "#allowed_acl" do + context "correctly sets" do + + it ":read rights" do + resource.rights(:read, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_perms)) + end + + it ":read_execute rights" do + resource.rights(:read_execute, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_execute_perms)) + end + + it ":write rights" do + resource.rights(:write, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_write_perms, write_flag)) + end + + it ":modify rights" do + resource.rights(:modify, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_modify_perms)) + end + + it ":full_control rights" do + resource.rights(:full_control, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_full_control_perms)) + end + + it "multiple rights" do + resource.rights(:read, "Everyone") + resource.rights(:modify, "Guest") + resource.run_action(:create) + + expect(explicit_aces).to eq( + allowed_acl(SID.Everyone, expected_read_perms) + + allowed_acl(SID.Guest, expected_modify_perms) + ) + end end + end - it "correctly sets :read_execute rights" do - resource.rights(:read_execute, "Guest") - resource.run_action(:create) - expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_read_execute_perms)) - end - - it "correctly sets :write rights" do - resource.rights(:write, "Guest") - resource.run_action(:create) - expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_write_perms)) - end - - it "correctly sets :modify rights" do - resource.rights(:modify, "Guest") - resource.run_action(:create) - expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_modify_perms)) - end - - it "correctly sets :full_control rights" do - resource.rights(:full_control, "Guest") - resource.run_action(:create) - expect(explicit_aces).to eq(allowed_acl(SID.Guest, expected_full_control_perms)) - end - - it "correctly sets deny_rights" do - # deny is an ACE with full rights, but is a deny type ace, not an allow type - resource.deny_rights(:full_control, "Guest") - resource.run_action(:create) - expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_full_control_perms)) - end - - it "Sets multiple rights" do - resource.rights(:read, "Everyone") - resource.rights(:modify, "Guest") - resource.run_action(:create) - - expect(explicit_aces).to eq( - allowed_acl(SID.Everyone, expected_read_perms) + - allowed_acl(SID.Guest, expected_modify_perms) - ) - end - - it "Sets deny_rights ahead of rights" do - resource.rights(:read, "Everyone") - resource.deny_rights(:modify, "Guest") - resource.run_action(:create) - - expect(explicit_aces).to eq( - denied_acl(SID.Guest, expected_modify_perms) + - allowed_acl(SID.Everyone, expected_read_perms) - ) - end - - it "Sets deny_rights ahead of rights when specified in reverse order" do - resource.deny_rights(:modify, "Guest") - resource.rights(:read, "Everyone") - resource.run_action(:create) - - expect(explicit_aces).to eq( - denied_acl(SID.Guest, expected_modify_perms) + - allowed_acl(SID.Everyone, expected_read_perms) - ) + describe "#denied_acl" do + context "correctly sets" do + + it ":read rights" do + resource.deny_rights(:read, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_read_perms)) + end + + it ":read_execute rights" do + resource.deny_rights(:read_execute, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_read_execute_perms)) + end + + it ":write rights" do + resource.deny_rights(:write, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_write_perms, write_flag)) + end + + it ":modify rights" do + resource.deny_rights(:modify, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_modify_perms)) + end + + it ":full_control rights" do + # deny is an ACE with full rights, but is a deny type ace, not an allow type + resource.deny_rights(:full_control, "Guest") + resource.run_action(:create) + expect(explicit_aces).to eq(denied_acl(SID.Guest, expected_full_control_perms)) + end + + it "deny_rights ahead of rights" do + resource.rights(:read, "Everyone") + resource.deny_rights(:modify, "Guest") + resource.run_action(:create) + + expect(explicit_aces).to eq( + denied_acl(SID.Guest, expected_modify_perms) + + allowed_acl(SID.Everyone, expected_read_perms) + ) + end + + it "deny_rights ahead of rights when specified in reverse order" do + resource.deny_rights(:modify, "Guest") + resource.rights(:read, "Everyone") + resource.run_action(:create) + + expect(explicit_aces).to eq( + denied_acl(SID.Guest, expected_modify_perms) + + allowed_acl(SID.Everyone, expected_read_perms) + ) + end end - end context "with a mode attribute" do diff --git a/spec/support/shared/integration/integration_helper.rb b/spec/support/shared/integration/integration_helper.rb index b6851f2d0e..5fc9de4de7 100644 --- a/spec/support/shared/integration/integration_helper.rb +++ b/spec/support/shared/integration/integration_helper.rb @@ -19,7 +19,6 @@ require "tmpdir" require "fileutils" -require "chef_core/text" require "chef/config" require "chef/json_compat" require "chef/server_api" diff --git a/spec/unit/knife/bootstrap/train_connector_spec.rb b/spec/unit/knife/bootstrap/train_connector_spec.rb new file mode 100644 index 0000000000..08bf21dd42 --- /dev/null +++ b/spec/unit/knife/bootstrap/train_connector_spec.rb @@ -0,0 +1,155 @@ +# +# Copyright:: Copyright (c) 2019 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. +# + +require "spec_helper" +require "ostruct" +require "chef/knife/bootstrap/train_connector" + +describe Chef::Knife::Bootstrap::TrainConnector do + let(:protocol) { "mock" } + let(:family) { "unknown" } + let(:release) { "unknown" } # version + let(:name) { "unknown" } + let(:arch) { "x86_64" } + let(:host_url) { "mock://user1@example.com" } + let(:opts) { {} } + subject do + # Create a valid TargetHost with the backend stubbed out. + Chef::Knife::Bootstrap::TrainConnector.test_instance(host_url, + protocol: protocol, + family: family, + name: name, + release: release, + arch: arch, + opts: opts) + end + + context "connect!" do + end + + describe "platform helpers" do + context "on linux" do + let(:family) { "debian" } + let(:name) { "ubuntu" } + it "reports that it is linux and unix, because that is how train classifies it" do + expect(subject.unix?).to eq true + expect(subject.linux?).to eq true + expect(subject.windows?).to eq false + end + end + context "on unix" do + let(:family) { "os" } + let(:name) { "mac_os_x" } + it "reports only a unix OS" do + expect(subject.unix?).to eq true + expect(subject.linux?).to eq false + expect(subject.windows?).to eq false + end + end + context "on windows" do + let(:family) { "windows" } + let(:name) { "windows" } + it "reports only a windows OS" do + expect(subject.unix?).to eq false + expect(subject.linux?).to eq false + expect(subject.windows?).to eq true + end + end + end + + describe "#connect!" do + it "establishes the connection to the remote host by waiting for it" do + expect(subject.connection).to receive(:wait_until_ready) + subject.connect! + end + end + + describe "#temp_dir" do + context "under windows" do + let(:family) { "windows" } + let(:name) { "windows" } + + it "uses the windows command to create the temp dir" do + expected_command = Chef::Knife::Bootstrap::TrainConnector::MKTEMP_WIN_COMMAND + expect(subject).to receive(:run_command!).with(expected_command) + .and_return double("result", stdout: "C:/a/path") + expect(subject.temp_dir).to eq "C:/a/path" + end + + end + context "under linux and unix-like" do + let(:family) { "debian" } + let(:name) { "ubuntu" } + it "uses the *nix command to create the temp dir and sets ownership to logged-in user" do + expected_command = Chef::Knife::Bootstrap::TrainConnector::MKTEMP_NIX_COMMAND + expect(subject).to receive(:run_command!).with(expected_command) + .and_return double("result", stdout: "/a/path") + expect(subject).to receive(:run_command!).with("chown user1 '/a/path'") + expect(subject.temp_dir).to eq "/a/path" + end + + end + end + context "#upload_file_content!" do + it "creates a local file with expected content and uploads it" do + expect(subject).to receive(:upload_file!) do |local_path, remote_path| + expect(File.read(local_path)).to eq "test data" + expect(remote_path).to eq "/target/path" + end + subject.upload_file_content!("test data", "/target/path") + end + end + + context "del_file" do + context "on windows" do + let(:family) { "windows" } + let(:name) { "windows" } + it "deletes the file with a windows command" do + expect(subject).to receive(:run_command!) do |cmd, &_handler| + expect(cmd).to match(/Test-Path "deleteme\.txt".*/) + end + subject.del_file!("deleteme.txt") + end + end + context "on unix-like" do + let(:family) { "debian" } + let(:name) { "ubuntu" } + it "deletes the file with a windows command" do + expect(subject).to receive(:run_command!) do |cmd, &_handler| + expect(cmd).to match(/rm -f "deleteme\.txt".*/) + end + subject.del_file!("deleteme.txt") + end + end + end + + context "#run_command!" do + it "raises a RemoteExecutionFailed when the remote execution failed" do + command_result = double("results", stdout: "", stderr: "failed", exit_status: 1) + expect(subject).to receive(:run_command).and_return command_result + + expect { subject.run_command!("test") }.to raise_error do |e| + expect(e.hostname).to eq subject.hostname + expect(e.class).to eq Chef::Knife::Bootstrap::RemoteExecutionFailed + expect(e.stderr).to eq "failed" + expect(e.stdout).to eq "" + expect(e.exit_status).to eq 1 + end + end + end + +end diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb index f54c8ac1d6..ce590fc9ee 100644 --- a/spec/unit/knife/bootstrap_spec.rb +++ b/spec/unit/knife/bootstrap_spec.rb @@ -25,8 +25,17 @@ describe Chef::Knife::Bootstrap do let(:bootstrap_template) { nil } let(:stderr) { StringIO.new } let(:bootstrap_cli_options) { [ ] } - let(:base_os) { :linux } - let(:target_host) { double("TargetHost") } + let(:linux_test) { true } + let(:windows_test) { false } + let(:linux_test) { false } + let(:unix_test) { false } + let(:ssh_test) { false } + + let(:connection) do + double("TrainConnector", + windows?: windows_test, + linux?: linux_test, + unix?: unix_test) end let(:knife) do Chef::Log.logger = Logger.new(StringIO.new) @@ -35,15 +44,11 @@ describe Chef::Knife::Bootstrap do k = Chef::Knife::Bootstrap.new(bootstrap_cli_options) allow(k.ui).to receive(:stderr).and_return(stderr) allow(k).to receive(:encryption_secret_provided_ignore_encrypt_flag?).and_return(false) - allow(k).to receive(:target_host).and_return target_host + allow(k).to receive(:connection).and_return connection k.merge_configs k end - before do - allow(target_host).to receive(:base_os).and_return base_os - end - context "#bootstrap_template" do it "should default to chef-full" do expect(knife.bootstrap_template).to be_a_kind_of(String) @@ -320,7 +325,7 @@ describe Chef::Knife::Bootstrap do subject(:knife) do k = described_class.new Chef::Config[:knife][:bootstrap_template] = template_file - allow(k).to receive(:target_host).and_return target_host + allow(k).to receive(:connection).and_return connection k.parse_options(options) k.merge_configs k @@ -1578,7 +1583,7 @@ describe Chef::Knife::Bootstrap do expect(knife).to receive(:render_template).and_return "content" expect(knife).to receive(:upload_bootstrap).with("content").and_return "/remote/path.sh" expect(knife).to receive(:perform_bootstrap).with("/remote/path.sh") - expect(target_host).to receive(:del_file) # Make sure cleanup happens + expect(connection).to receive(:del_file!) # Make sure cleanup happens knife.run @@ -1687,14 +1692,14 @@ describe Chef::Knife::Bootstrap do let(:result_mock) { double("result", exit_status: exit_status, stderr: "A message") } before do - allow(target_host).to receive(:hostname).and_return "testhost" + allow(connection).to receive(:hostname).and_return "testhost" end it "runs the remote script and logs the output" do expect(knife.ui).to receive(:info).with(/Bootstrapping.*/) expect(knife).to receive(:bootstrap_command) .with("/path.sh") .and_return("sh /path.sh") - expect(target_host) + expect(connection) .to receive(:run_command) .with("sh /path.sh") .and_yield("output here") @@ -1710,7 +1715,7 @@ describe Chef::Knife::Bootstrap do expect(knife).to receive(:bootstrap_command) .with("/path.sh") .and_return("sh /path.sh") - expect(target_host).to receive(:run_command).with("sh /path.sh").and_return result_mock + expect(connection).to receive(:run_command).with("sh /path.sh").and_return result_mock expect { knife.perform_bootstrap("/path.sh") }.to raise_error(SystemExit) end end @@ -1738,13 +1743,11 @@ describe Chef::Knife::Bootstrap do context "when an auth failure occurs" do let(:expected_error) do - # TODO This is awkward and ugly. Requires some refactor of chef_core/error - # to make it not so. See comment in rescue block of connect! for details. - e = RuntimeError.new - interim = RuntimeError.new + e = Train::Error.new actual = Net::SSH::AuthenticationFailed.new - allow(interim).to receive(:cause).and_return(actual) - allow(e).to receive(:cause).and_return(interim) + # Simulate train's nested error - they wrap + # ssh/network errors in a TrainError. + allow(e).to receive(:cause).and_return(actual) e end @@ -1754,7 +1757,7 @@ describe Chef::Knife::Bootstrap do context "and password auth was used" do before do - knife.config[:connection_password] = "tryme" + allow(connection).to receive(:password_auth?).and_return true end it "re-raises the error so as not to resubmit the same failing password" do @@ -1765,8 +1768,8 @@ describe Chef::Knife::Bootstrap do context "and password auth was not used" do before do - knife.config.delete :connection_password - allow(target_host).to receive(:user).and_return "testuser" + allow(connection).to receive(:password_auth?).and_return false + allow(connection).to receive(:user).and_return "testuser" end it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do @@ -1793,7 +1796,7 @@ describe Chef::Knife::Bootstrap do describe "#bootstrap_context" do context "under Windows" do - let(:base_os) { :windows } + let(:windows_test) { true } it "creates a WindowsBootstrapContext" do require "chef/knife/core/windows_bootstrap_context" expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::WindowsBootstrapContext @@ -1801,7 +1804,7 @@ describe Chef::Knife::Bootstrap do end context "under linux" do - let(:base_os) { :linux } + let(:linux_test) { true } it "creates a BootstrapContext" do require "chef/knife/core/bootstrap_context" expect(knife.bootstrap_context.class).to eq Chef::Knife::Core::BootstrapContext @@ -1841,25 +1844,25 @@ describe Chef::Knife::Bootstrap do describe "#upload_bootstrap" do before do - allow(target_host).to receive(:temp_dir).and_return(temp_dir) - allow(target_host).to receive(:normalize_path) { |a| a } + allow(connection).to receive(:temp_dir).and_return(temp_dir) + allow(connection).to receive(:normalize_path) { |a| a } end let(:content) { "bootstrap script content" } context "under Windows" do - let(:base_os) { :windows } + let(:windows_test) { true } let(:temp_dir) { "C:/Temp/bootstrap" } - it "creates a bat file in the temp dir provided by target_host, using given content" do - expect(target_host).to receive(:save_as_remote_file).with(content, "C:/Temp/bootstrap/bootstrap.bat") + it "creates a bat file in the temp dir provided by connection, using given content" do + expect(connection).to receive(:upload_file_content!).with(content, "C:/Temp/bootstrap/bootstrap.bat") expect(knife.upload_bootstrap(content)).to eq "C:/Temp/bootstrap/bootstrap.bat" end end context "under Linux" do - let(:base_os) { :linux } + let(:linux_test) { true } let(:temp_dir) { "/tmp/bootstrap" } - it "creates a 'sh file in the temp dir provided by target_host, using given content" do - expect(target_host).to receive(:save_as_remote_file).with(content, "/tmp/bootstrap/bootstrap.sh") + it "creates a 'sh file in the temp dir provided by connection, using given content" do + expect(connection).to receive(:upload_file_content!).with(content, "/tmp/bootstrap/bootstrap.sh") expect(knife.upload_bootstrap(content)).to eq "/tmp/bootstrap/bootstrap.sh" end end @@ -1867,14 +1870,14 @@ describe Chef::Knife::Bootstrap do describe "#bootstrap_command" do context "under Windows" do - let(:base_os) { :windows } + let(:windows_test) { true } it "prefixes the command to run under cmd.exe" do expect(knife.bootstrap_command("autoexec.bat")).to eq "cmd.exe /C autoexec.bat" end end context "under Linux" do - let(:base_os) { :linux } + let(:linux_test) { true } it "prefixes the command to run under sh" do expect(knife.bootstrap_command("bootstrap")).to eq "sh bootstrap" end @@ -1883,14 +1886,14 @@ describe Chef::Knife::Bootstrap do describe "#default_bootstrap_template" do context "under Windows" do - let(:base_os) { :windows } + let(:windows_test) { true } it "is windows-chef-client-msi" do expect(knife.default_bootstrap_template).to eq "windows-chef-client-msi" end end context "under Linux" do - let(:base_os) { :linux } + let(:linux_test) { true } it "is chef-full" do expect(knife.default_bootstrap_template).to eq "chef-full" end @@ -1899,15 +1902,15 @@ describe Chef::Knife::Bootstrap do describe "#do_connect" do let(:host_descriptor) { "example.com" } - let(:target_host) { double("TargetHost") } - let(:resolver_mock) { double("TargetResolver", targets: [ target_host ]) } + let(:connection) { double("TrainConnector") } + let(:connector_mock) { double("TargetResolver", targets: [ connection ]) } before do allow(knife).to receive(:host_descriptor).and_return host_descriptor end - it "resolves the target and connects it" do - expect(ChefCore::TargetResolver).to receive(:new).and_return resolver_mock - expect(target_host).to receive(:connect!) + it "creates a TrainConnector and connects it" do + expect(Chef::Knife::Bootstrap::TrainConnector).to receive(:new).and_return connection + expect(connection).to receive(:connect!) knife.do_connect({}) end end diff --git a/spec/unit/resource/windows_task_spec.rb b/spec/unit/resource/windows_task_spec.rb index 96482d3d56..b152d879f6 100644 --- a/spec/unit/resource/windows_task_spec.rb +++ b/spec/unit/resource/windows_task_spec.rb @@ -61,6 +61,10 @@ describe Chef::Resource::WindowsTask, :windows_only do expect(resource.stop_if_going_on_batteries).to eql(false) end + it "sets the default value for start_when_available as false" do + expect(resource.start_when_available).to eql(false) + end + context "when frequency is not provided" do it "raises ArgumentError to provide frequency" do expect { resource.after_created }.to raise_error(ArgumentError, "Frequency needs to be provided. Valid frequencies are :minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none." ) |