diff options
72 files changed, 1007 insertions, 492 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a15e5747cc..aca7c10237 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,6 +11,5 @@ StackOverflow discussion that's relevant] - [ ] New functionality includes tests - [ ] All tests pass -- [ ] Documentation, especially RELEASE\_NOTES.md, has been updated if - required +- [ ] RELEASE\_NOTES.md, has been updated if required (not required for bugfixes, required for API changes) - [ ] All commits have been signed-off for the Developer Certificate of Origin. See <https://github.com/chef/chef/blob/master/CONTRIBUTING.md#developer-certification-of-origin-dco> diff --git a/CHANGELOG.md b/CHANGELOG.md index d940293470..d414d611e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -726,7 +726,7 @@ of partial templates. Typo fixes * [**Tim Smith**](https://github.com/tas50) Typo fixes -* [Pull 2505](https://github.com/opscode/chef/pull/2505) Make Chef handle URIs in a case-insensitive manner +* [Pull 2505](https://github.com/chef/chef/pull/2505) Make Chef handle URIs in a case-insensitive manner * [**Phil Dibowitz**](https://github.com/jaymzh): Drop SSL warnings now that we have a safe default * [Pull 2684](https://github.com/chef/chef/pull/2684) Remove ole_initialize/uninitialize which cause problems with Ruby >= 2 @@ -812,32 +812,32 @@ of partial templates. ## 12.0.3 * [**Phil Dibowitz**](https://github.com/jaymzh): -[Issue 2594](https://github.com/opscode/chef/issues/2594) Restore missing require in `digester`. +[Issue 2594](https://github.com/chef/chef/issues/2594) Restore missing require in `digester`. ## 12.0.2 -* [Issue 2578](https://github.com/opscode/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider -* [Issue 2609](https://github.com/opscode/chef/issues/2609) Resolve the circular dependency between ProviderResolver and Resource. -* [Issue 2596](https://github.com/opscode/chef/issues/2596) Fix nodes not writing to disk -* [Issue 2580](https://github.com/opscode/chef/issues/2580) Make sure the relative paths are preserved when using link resource. -* [Pull 2630](https://github.com/opscode/chef/pull/2630) Improve knife's SSL error messaging -* [Issue 2606](https://github.com/opscode/chef/issues/2606) chef 12 ignores default_release for apt_package -* [Issue 2602](https://github.com/opscode/chef/issues/2602) Fix `subscribes` resource notifications. -* [Issue 2578](https://github.com/opscode/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider. +* [Issue 2578](https://github.com/chef/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider +* [Issue 2609](https://github.com/chef/chef/issues/2609) Resolve the circular dependency between ProviderResolver and Resource. +* [Issue 2596](https://github.com/chef/chef/issues/2596) Fix nodes not writing to disk +* [Issue 2580](https://github.com/chef/chef/issues/2580) Make sure the relative paths are preserved when using link resource. +* [Pull 2630](https://github.com/chef/chef/pull/2630) Improve knife's SSL error messaging +* [Issue 2606](https://github.com/chef/chef/issues/2606) chef 12 ignores default_release for apt_package +* [Issue 2602](https://github.com/chef/chef/issues/2602) Fix `subscribes` resource notifications. +* [Issue 2578](https://github.com/chef/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider. * [**gh2k**](https://github.com/gh2k): - [Issue 2625](https://github.com/opscode/chef/issues/2625) Fix missing `shell_out!` for `windows_package` resource + [Issue 2625](https://github.com/chef/chef/issues/2625) Fix missing `shell_out!` for `windows_package` resource * [**BackSlasher**](https://github.com/BackSlasher): - [Issue 2634](https://github.com/opscode/chef/issues/2634) Fix `option ':command' is not a valid option` error in subversion provider. + [Issue 2634](https://github.com/chef/chef/issues/2634) Fix `option ':command' is not a valid option` error in subversion provider. * [**Seth Vargo**](https://github.com/sethvargo): - [Issue 2345](https://github.com/opscode/chef/issues/2345) Allow knife to install cookbooks with metadata.json. + [Issue 2345](https://github.com/chef/chef/issues/2345) Allow knife to install cookbooks with metadata.json. ## 12.0.1 -* [Issue 2552](https://github.com/opscode/chef/issues/2552) Create constant for LWRP before calling `provides` -* [Issue 2545](https://github.com/opscode/chef/issues/2545) `path` attribute of `execute` resource is restored to provide backwards compatibility with Chef 11. -* [Issue 2565](https://github.com/opscode/chef/issues/2565) Fix `Chef::Knife::Core::BootstrapContext` constructor for knife-windows compat. -* [Issue 2566](https://github.com/opscode/chef/issues/2566) Make sure Client doesn't raise error when interval is set on Windows. -* [Issue 2560](https://github.com/opscode/chef/issues/2560) Fix `uninitialized constant Windows::Constants` in `windows_eventlog`. -* [Issue 2563](https://github.com/opscode/chef/issues/2563) Make sure the Chef Client rpm packages are signed with GPG keys correctly. +* [Issue 2552](https://github.com/chef/chef/issues/2552) Create constant for LWRP before calling `provides` +* [Issue 2545](https://github.com/chef/chef/issues/2545) `path` attribute of `execute` resource is restored to provide backwards compatibility with Chef 11. +* [Issue 2565](https://github.com/chef/chef/issues/2565) Fix `Chef::Knife::Core::BootstrapContext` constructor for knife-windows compat. +* [Issue 2566](https://github.com/chef/chef/issues/2566) Make sure Client doesn't raise error when interval is set on Windows. +* [Issue 2560](https://github.com/chef/chef/issues/2560) Fix `uninitialized constant Windows::Constants` in `windows_eventlog`. +* [Issue 2563](https://github.com/chef/chef/issues/2563) Make sure the Chef Client rpm packages are signed with GPG keys correctly. ## 12.0.0 @@ -950,7 +950,7 @@ of partial templates. * [**Ionuț Arțăriși**](https://github.com/mapleoin): Changed the default group provider to gpasswd on SLES versions 12 and higher. * [**Noah Kantrowitz**](https://github.com/coderanger): - Implemented [RFC017 - File Specificity Overhaul](https://github.com/opscode/chef-rfc/blob/master/rfc017-file-specificity.md). + Implemented [RFC017 - File Specificity Overhaul](https://github.com/chef/chef-rfc/blob/master/rfc017-file-specificity.md). * [**James Bence**](https://github.com/jbence): Improved the reliability of Git provider by making it to be more specific when selecting tags. * [**Jean Mertz**](https://github.com/JeanMertz): @@ -988,7 +988,7 @@ of partial templates. ### Chef Contributions * ruby 1.9.3 support is dropped -* Added RFC-023 Chef 12 Attribute Changes (https://github.com/opscode/chef-rfc/blob/master/rfc023-chef-12-attributes-changes.md) +* Added RFC-023 Chef 12 Attribute Changes (https://github.com/chef/chef-rfc/blob/master/rfc023-chef-12-attributes-changes.md) * Added os/platform_family options to provides syntax on the Chef::Resource DSL * Added provides methods to the Chef::Provider DSL * Added supported?(resource, action) class method to all Providers for late-evaluation if a provider can handle a diff --git a/Gemfile.lock b/Gemfile.lock index 9effc84d42..2fd38c1bc1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/chef/chef-server - revision: 6fde9680c6efdcd0792bec840b7fe7f6fbee790f + revision: 77a662df32c79865b2195cc398d7e121c7048ff3 specs: oc-chef-pedant (2.2.0) activesupport (>= 4.2.7.1, < 6.0) @@ -16,7 +16,7 @@ GIT GIT remote: https://github.com/chef/chefstyle.git - revision: c36dcbd6c2c21d2e19db77d9fbdf2402d0bacccf + revision: 6d24969bd7c5471e089bd47f45e4832d03e9f8a2 branch: master specs: chefstyle (0.4.0) @@ -33,10 +33,10 @@ GIT PATH remote: . specs: - chef (12.15.26) + chef (12.16.23) addressable bundler (>= 1.10) - chef-config (= 12.15.26) + chef-config (= 12.16.23) chef-zero (>= 4.8) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -62,10 +62,10 @@ PATH specinfra (~> 2.10) syslog-logger (~> 1.6) uuidtools (~> 2.1.5) - chef (12.15.26-universal-mingw32) + chef (12.16.23-universal-mingw32) addressable bundler (>= 1.10) - chef-config (= 12.15.26) + chef-config (= 12.16.23) chef-zero (>= 4.8) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -106,7 +106,7 @@ PATH PATH remote: chef-config specs: - chef-config (12.15.26) + chef-config (12.16.23) addressable fuzzyurl mixlib-config (~> 2.0) @@ -125,12 +125,12 @@ GEM mixlib-cli (~> 1.4) artifactory (2.5.0) ast (2.3.0) - aws-sdk (2.6.5) - aws-sdk-resources (= 2.6.5) - aws-sdk-core (2.6.5) + aws-sdk (2.6.9) + aws-sdk-resources (= 2.6.9) + aws-sdk-core (2.6.9) jmespath (~> 1.0) - aws-sdk-resources (2.6.5) - aws-sdk-core (= 2.6.5) + aws-sdk-resources (2.6.9) + aws-sdk-core (= 2.6.9) aws-sdk-v1 (1.66.0) json (~> 1.4) nokogiri (>= 1.4.4) @@ -178,6 +178,7 @@ GEM url coderay (1.1.1) concurrent-ruby (1.0.2) + connection_pool (2.2.0) cucumber (2.4.0) builder (>= 2.1.2) cucumber-core (~> 1.5.0) @@ -299,10 +300,11 @@ GEM mixlib-log mixlib-cli (1.7.0) mixlib-config (2.2.4) - mixlib-install (2.0.3) + mixlib-install (2.1.1) artifactory mixlib-shellout mixlib-versioning + thor mixlib-log (1.7.1) mixlib-shellout (2.2.7) mixlib-shellout (2.2.7-universal-mingw32) @@ -313,7 +315,8 @@ GEM multi_test (0.1.2) multi_xml (0.5.5) multipart-post (2.0.0) - net-http-persistent (2.9.4) + net-http-persistent (3.0.0) + connection_pool (~> 2.2) net-http-pipeline (1.0.1) net-http-spy (0.2.1) net-scp (1.2.1) @@ -328,12 +331,10 @@ GEM net-ssh-gateway (>= 1.2.0) net-telnet (0.1.1) netrc (0.11.0) - nokogiri (1.6.8) + nokogiri (1.6.8.1) mini_portile2 (~> 2.1.0) - pkg-config (~> 1.1.7) - nokogiri (1.6.8-x86-mingw32) + nokogiri (1.6.8.1-x86-mingw32) mini_portile2 (~> 2.1.0) - pkg-config (~> 1.1.7) nori (2.6.0) oauth2 (1.2.0) faraday (>= 0.8, < 0.10) @@ -357,16 +358,15 @@ GEM wmi-lite (~> 1.0) parser (2.3.1.4) ast (~> 2.2) - pkg-config (1.1.7) plist (3.2.0) poise (2.7.1) halite (~> 1.0) - poise-boiler (1.13.0) + poise-boiler (1.13.1) bundler chefspec (~> 5.0) codeclimate-test-reporter (~> 0.4) codecov (~> 0.0, >= 0.0.2) - foodcritic (~> 8.0) + foodcritic (>= 7, < 9) fuubar (~> 2.0) git (~> 1.2) halite (~> 1.2) @@ -461,19 +461,19 @@ GEM sawyer (0.7.0) addressable (>= 2.3.5, < 2.5) faraday (~> 0.8, < 0.10) - serverspec (2.36.1) + serverspec (2.37.1) multi_json rspec (~> 3.0) rspec-its specinfra (~> 2.53) - sfl (2.2) + sfl (2.3) simplecov (0.12.0) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.0) slop (3.6.0) - specinfra (2.63.1) + specinfra (2.63.3) net-scp net-ssh (>= 2.7, < 4.0) net-telnet @@ -539,7 +539,7 @@ GEM ffi-win32-extensions windows-api (0.4.4) win32-api (>= 1.4.5) - winrm (2.0.3) + winrm (2.1.0) builder (>= 2.1.2) erubis (~> 2.7) gssapi (~> 1.2) @@ -1 +1 @@ -12.15.26
\ No newline at end of file +12.16.23
\ No newline at end of file diff --git a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml b/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml index 209b7fa979..caefef49ea 100644 --- a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml +++ b/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml @@ -10,7 +10,9 @@ end driver: name: ec2 tags: - X-Project: chef-ci-acceptance + X-Dept: Eng + X-Contact: product-team + X-Application: chef-ci-acceptance aws_ssh_key_id: <%= ENV['AWS_SSH_KEY_ID'] || ENV['USER'] || ENV['USERNAME'] %> # test-specific stuff region: us-west-2 diff --git a/acceptance/Gemfile.lock b/acceptance/Gemfile.lock index 55da7b77a8..8fc49431b7 100644 --- a/acceptance/Gemfile.lock +++ b/acceptance/Gemfile.lock @@ -11,12 +11,12 @@ GEM specs: addressable (2.4.0) artifactory (2.5.0) - aws-sdk (2.6.5) - aws-sdk-resources (= 2.6.5) - aws-sdk-core (2.6.5) + aws-sdk (2.6.9) + aws-sdk-resources (= 2.6.9) + aws-sdk-core (2.6.9) jmespath (~> 1.0) - aws-sdk-resources (2.6.5) - aws-sdk-core (= 2.6.5) + aws-sdk-resources (2.6.9) + aws-sdk-core (= 2.6.9) berkshelf (4.3.5) addressable (~> 2.3, >= 2.3.4) berkshelf-api-client (~> 2.0, >= 2.0.2) @@ -53,7 +53,7 @@ GEM celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.14.89) + chef-config (12.15.19) addressable fuzzyurl mixlib-config (~> 2.0) @@ -61,7 +61,7 @@ GEM cleanroom (1.0.0) coderay (1.1.1) diff-lcs (1.2.5) - docker-api (1.32.0) + docker-api (1.32.1) excon (>= 0.38.0) json erubis (2.7.0) @@ -77,7 +77,7 @@ GEM hashie (3.4.6) hitimes (1.2.4) httpclient (2.7.2) - inspec (1.0.0) + inspec (1.2.0) hashie (~> 3.4) json (>= 1.8, < 3.0) method_source (~> 0.8) @@ -211,7 +211,7 @@ GEM hashie (>= 2.0.2, < 4.0.0) windows_chef_zero (2.0.0) test-kitchen (>= 1.2.1) - winrm (2.0.3) + winrm (2.1.0) builder (>= 2.1.2) erubis (~> 2.7) gssapi (~> 1.2) diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index f2db54aa17..2fe59c8350 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -32,6 +32,7 @@ require "mixlib/shellout" require "uri" require "addressable/uri" require "openssl" +require "yaml" module ChefConfig @@ -70,6 +71,25 @@ module ChefConfig event_handlers << logger end + def self.apply_extra_config_options(extra_config_options) + if extra_config_options + extra_parsed_options = extra_config_options.inject({}) do |memo, option| + # Sanity check value. + if option.empty? || !option.include?("=") + raise UnparsableConfigOption, "Unparsable config option #{option.inspect}" + end + # Split including whitespace if someone does truly odd like + # --config-option "foo = bar" + key, value = option.split(/\s*=\s*/, 2) + # Call to_sym because Chef::Config expects only symbol keys. Also + # runs a simple parse on the string for some common types. + memo[key.to_sym] = YAML.safe_load(value) + memo + end + merge!(extra_parsed_options) + end + end + # Config file to load (client.rb, knife.rb, etc. defaults set differently in knife, chef-client, etc.) configurable(:config_file) @@ -966,7 +986,7 @@ module ChefConfig # If there is no 'locale -a' then we return 'en_US.UTF-8' since that is the most commonly # available English UTF-8 locale. However, all modern POSIXen should support 'locale -a'. def self.guess_internal_locale - # https://github.com/opscode/chef/issues/2181 + # https://github.com/chef/chef/issues/2181 # Some systems have the `locale -a` command, but the result has # invalid characters for the default encoding. # diff --git a/chef-config/lib/chef-config/exceptions.rb b/chef-config/lib/chef-config/exceptions.rb index db10a5f364..23fd28f9c8 100644 --- a/chef-config/lib/chef-config/exceptions.rb +++ b/chef-config/lib/chef-config/exceptions.rb @@ -22,5 +22,6 @@ module ChefConfig class ConfigurationError < ArgumentError; end class InvalidPath < StandardError; end + class UnparsableConfigOption < StandardError; end end diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index acbf48ea49..ebc1c76739 100644 --- a/chef-config/lib/chef-config/version.rb +++ b/chef-config/lib/chef-config/version.rb @@ -21,7 +21,7 @@ module ChefConfig CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.15.26" + VERSION = "12.16.23" end # diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb index 0ddb56cf0d..2cdf9af78f 100644 --- a/chef-config/spec/unit/config_spec.rb +++ b/chef-config/spec/unit/config_spec.rb @@ -68,6 +68,91 @@ RSpec.describe ChefConfig::Config do end end + describe "parsing arbitrary config from the CLI" do + + def apply_config + described_class.apply_extra_config_options(extra_config_options) + end + + context "when no arbitrary config is given" do + + let(:extra_config_options) { nil } + + it "succeeds" do + expect { apply_config }.to_not raise_error + end + + end + + context "when given a simple string option" do + + let(:extra_config_options) { [ "node_name=bobotclown" ] } + + it "applies the string option" do + apply_config + expect(described_class[:node_name]).to eq("bobotclown") + end + + end + + context "when given a blank value" do + + let(:extra_config_options) { [ "http_retries=" ] } + + it "sets the value to nil" do + # ensure the value is actually changed in the test + described_class[:http_retries] = 55 + apply_config + expect(described_class[:http_retries]).to eq(nil) + end + end + + context "when given spaces between `key = value`" do + + let(:extra_config_options) { [ "node_name = bobo" ] } + + it "handles the extra spaces and applies the config option" do + apply_config + expect(described_class[:node_name]).to eq("bobo") + end + + end + + context "when given an integer value" do + + let(:extra_config_options) { [ "http_retries=9000" ] } + + it "converts to a numeric type and applies the config option" do + apply_config + expect(described_class[:http_retries]).to eq(9000) + end + + end + + context "when given a boolean" do + + let(:extra_config_options) { [ "boolean_thing=true" ] } + + it "converts to a boolean type and applies the config option" do + apply_config + expect(described_class[:boolean_thing]).to eq(true) + end + + end + + context "when given input that is not in key=value form" do + + let(:extra_config_options) { [ "http_retries:9000" ] } + + it "raises UnparsableConfigOption" do + message = 'Unparsable config option "http_retries:9000"' + expect { apply_config }.to raise_error(ChefConfig::UnparsableConfigOption, message) + end + + end + + end + describe "when configuring formatters" do # if TTY and not(force-logger) # formatter = configured formatter or default formatter diff --git a/distro/common/html/knife_bootstrap.html b/distro/common/html/knife_bootstrap.html index 5e3d70404f..7589dfbaba 100644 --- a/distro/common/html/knife_bootstrap.html +++ b/distro/common/html/knife_bootstrap.html @@ -173,7 +173,7 @@ <div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>knife bootstrap 192.168.1.100 -r <span class="s1">'role[webserver]'</span> -d ubuntu12.04-gems-mine </pre></div> </div> -<p>Alternatively, an example bootstrap template can be found in the git source for the chef-repo: <a class="reference external" href="https://github.com/opscode/chef/tree/master/lib/chef/knife/bootstrap">https://github.com/opscode/chef/tree/master/lib/chef/knife/bootstrap</a>. Copy the template to <tt class="docutils literal"><span class="pre">~/.chef-repo/.chef/bootstrap/ubuntu12.04-apt.erb</span></tt> and modify the template appropriately.</p> +<p>Alternatively, an example bootstrap template can be found in the git source for the chef-repo: <a class="reference external" href="https://github.com/chef/chef/tree/master/lib/chef/knife/bootstrap">https://github.com/chef/chef/tree/master/lib/chef/knife/bootstrap</a>. Copy the template to <tt class="docutils literal"><span class="pre">~/.chef-repo/.chef/bootstrap/ubuntu12.04-apt.erb</span></tt> and modify the template appropriately.</p> </div> <div class="section" id="debian-and-apt"> <h3>Debian and Apt<a class="headerlink" href="#debian-and-apt" title="Permalink to this headline">¶</a></h3> diff --git a/distro/common/man/man1/README.md b/distro/common/man/man1/README.md index 9a915fb4cc..7af6aa8948 100644 --- a/distro/common/man/man1/README.md +++ b/distro/common/man/man1/README.md @@ -9,7 +9,7 @@ that are built into the chef-client are managed. ## Source Files The source files are located in the chef-docs repository: -https://github.com/opscode/chef-docs +https://github.com/chef/chef-docs Each Knife subcommand has its own source folder. The folder naming pattern begins with man_. @@ -55,4 +55,4 @@ at docs.opscode.com/knife_foo.html. ## Questions? -Open an [Issue](https://github.com/opscode/chef-docs/issues) and ask. +Open an [Issue](https://github.com/chef/chef-docs/issues) and ask. diff --git a/distro/common/man/man1/knife-client.1 b/distro/common/man/man1/knife-client.1 index 9dbd174c71..24bd999d65 100644 --- a/distro/common/man/man1/knife-client.1 +++ b/distro/common/man/man1/knife-client.1 @@ -37,7 +37,7 @@ However, during the first chef\-client run, this private key does not exist. Ins .sp During the initial chef\-client run, the chef\-client will register with the Chef server using the private key assigned to the chef\-validator, after which the chef\-client will obtain a \fBclient.pem\fP private key for all future authentication requests to the Chef server\&. .sp -After the initial chef\-client run has completed successfully, the chef\-validator is no longer required and may be deleted from the node. Use the \fBdelete_validation\fP recipe found in the \fBchef\-client\fP cookbook (\fI\%https://github.com/opscode\-cookbooks/chef\-client\fP) to remove the chef\-validator\&. +After the initial chef\-client run has completed successfully, the chef\-validator is no longer required and may be deleted from the node. Use the \fBdelete_validation\fP recipe found in the \fBchef\-client\fP cookbook (\fI\%https://github.com/chef\-cookbooks/chef\-client\fP) to remove the chef\-validator\&. .sp The \fBknife client\fP subcommand is used to manage an API client list and their associated RSA public key\-pairs. This allows authentication requests to be made to the Chef server by any entity that uses the Chef server API, such as the chef\-client and knife\&. .SH COMMON OPTIONS diff --git a/kitchen-tests/Berksfile.lock b/kitchen-tests/Berksfile.lock index f08e94dc94..9261f6f481 100644 --- a/kitchen-tests/Berksfile.lock +++ b/kitchen-tests/Berksfile.lock @@ -52,7 +52,7 @@ GRAPH sudo (>= 0.0.0) ubuntu (>= 0.0.0) users (>= 0.0.0) - build-essential (7.0.0) + build-essential (7.0.1) compat_resource (>= 12.14) mingw (>= 1.1) seven_zip (>= 0.0.0) @@ -64,16 +64,16 @@ GRAPH chef_hostname (0.4.2) compat_resource (>= 0.0.0) compat_resource (12.14.7) - cron (2.0.0) + cron (3.0.0) database (6.0.0) postgresql (>= 1.0.0) firewall (2.5.2) chef-sugar (>= 0.0.0) httpd (0.4.4) compat_resource (>= 12.14.6) - iis (5.0.1) + iis (5.0.4) windows (>= 1.34.6) - iptables (3.0.0) + iptables (3.0.1) compat_resource (>= 12.14.3) logrotate (2.1.0) compat_resource (>= 0.0.0) @@ -98,7 +98,8 @@ GRAPH ntp (3.2.0) openssh (2.1.0) iptables (>= 1.0) - openssl (6.0.0) + openssl (4.4.0) + chef-sugar (>= 3.1.1) php (1.5.0) build-essential (>= 0.0.0) iis (>= 0.0.0) @@ -106,10 +107,10 @@ GRAPH windows (>= 0.0.0) xml (>= 0.0.0) yum-epel (>= 0.0.0) - postgresql (3.4.16) + postgresql (4.0.6) apt (>= 1.9.0) build-essential (>= 0.0.0) - openssl (>= 0.0.0) + openssl (~> 4.0) rbac (1.0.3) resolver (1.3.1) selinux (0.9.0) diff --git a/kitchen-tests/Gemfile.lock b/kitchen-tests/Gemfile.lock index f1e544cc08..7498483862 100644 --- a/kitchen-tests/Gemfile.lock +++ b/kitchen-tests/Gemfile.lock @@ -3,12 +3,12 @@ GEM specs: addressable (2.4.0) artifactory (2.5.0) - aws-sdk (2.6.5) - aws-sdk-resources (= 2.6.5) - aws-sdk-core (2.6.5) + aws-sdk (2.6.9) + aws-sdk-resources (= 2.6.9) + aws-sdk-core (2.6.9) jmespath (~> 1.0) - aws-sdk-resources (2.6.5) - aws-sdk-core (= 2.6.5) + aws-sdk-resources (2.6.9) + aws-sdk-core (= 2.6.9) berkshelf (5.1.0) addressable (~> 2.3, >= 2.3.4) berkshelf-api-client (>= 2.0.2, < 4.0) @@ -43,7 +43,7 @@ GEM celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.14.89) + chef-config (12.15.19) addressable fuzzyurl mixlib-config (~> 2.0) @@ -51,7 +51,7 @@ GEM cleanroom (1.0.0) coderay (1.1.1) diff-lcs (1.2.5) - docker-api (1.32.0) + docker-api (1.32.1) excon (>= 0.38.0) json erubis (2.7.0) @@ -69,7 +69,7 @@ GEM hitimes (1.2.4) hitimes (1.2.4-x86-mingw32) httpclient (2.8.2.4) - inspec (1.0.0) + inspec (1.2.0) hashie (~> 3.4) json (>= 1.8, < 3.0) method_source (~> 0.8) @@ -86,7 +86,7 @@ GEM jmespath (1.3.1) json (2.0.2) kitchen-appbundle-updater (0.1.2) - kitchen-dokken (1.0.0) + kitchen-dokken (1.0.3) docker-api (~> 1.29) test-kitchen (~> 1.5) kitchen-ec2 (1.2.0) @@ -111,10 +111,11 @@ GEM mixlib-authentication (1.4.1) mixlib-log mixlib-config (2.2.4) - mixlib-install (2.0.3) + mixlib-install (2.1.1) artifactory mixlib-shellout mixlib-versioning + thor mixlib-log (1.7.1) mixlib-shellout (2.2.7) mixlib-shellout (2.2.7-universal-mingw32) @@ -211,7 +212,7 @@ GEM hashie (>= 2.0.2, < 4.0.0) win32-process (0.8.3) ffi (>= 1.0.0) - winrm (2.0.3) + winrm (2.1.0) builder (>= 2.1.2) erubis (~> 2.7) gssapi (~> 1.2) diff --git a/lib/chef/application.rb b/lib/chef/application.rb index f9735a3769..7f15859c8f 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -28,7 +28,6 @@ require "mixlib/cli" require "tmpdir" require "rbconfig" require "chef/application/exit_code" -require "yaml" class Chef class Application @@ -111,20 +110,13 @@ class Chef end extra_config_options = config.delete(:config_option) Chef::Config.merge!(config) - if extra_config_options - extra_parsed_options = extra_config_options.inject({}) do |memo, option| - # Sanity check value. - Chef::Application.fatal!("Unparsable config option #{option.inspect}") if option.empty? || !option.include?("=") - # Split including whitespace if someone does truly odd like - # --config-option "foo = bar" - key, value = option.split(/\s*=\s*/, 2) - # Call to_sym because Chef::Config expects only symbol keys. Also - # runs a simple parse on the string for some common types. - memo[key.to_sym] = YAML.safe_load(value) - memo - end - Chef::Config.merge!(extra_parsed_options) - end + apply_extra_config_options(extra_config_options) + end + + def apply_extra_config_options(extra_config_options) + Chef::Config.apply_extra_config_options(extra_config_options) + rescue ChefConfig::UnparsableConfigOption => e + Chef::Application.fatal!(e.message) end def set_specific_recipes diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index cbaa494b71..000aff905b 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -202,7 +202,7 @@ class Chef::Application::Client < Chef::Application :short => "-o RunlistItem,RunlistItem...", :long => "--override-runlist RunlistItem,RunlistItem...", :description => "Replace current run list with specified items for a single run", - :proc => lambda {|items| + :proc => lambda { |items| items = items.split(",") items.compact.map do |item| Chef::RunList::RunListItem.new(item) @@ -213,7 +213,7 @@ class Chef::Application::Client < Chef::Application :short => "-r RunlistItem,RunlistItem...", :long => "--runlist RunlistItem,RunlistItem...", :description => "Permanently replace current run list with specified items", - :proc => lambda {|items| + :proc => lambda { |items| items = items.split(",") items.compact.map do |item| Chef::RunList::RunListItem.new(item) diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb index 446a0f007d..1481338a9c 100644 --- a/lib/chef/application/solo.rb +++ b/lib/chef/application/solo.rb @@ -166,7 +166,7 @@ class Chef::Application::Solo < Chef::Application :short => "-o RunlistItem,RunlistItem...", :long => "--override-runlist RunlistItem,RunlistItem...", :description => "Replace current run list with specified items", - :proc => lambda {|items| + :proc => lambda { |items| items = items.split(",") items.compact.map do |item| Chef::RunList::RunListItem.new(item) diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb index f019448bd8..0bb15c03ca 100644 --- a/lib/chef/chef_class.rb +++ b/lib/chef/chef_class.rb @@ -30,6 +30,7 @@ require "chef/platform/provider_priority_map" require "chef/platform/resource_priority_map" require "chef/platform/provider_handler_map" require "chef/platform/resource_handler_map" +require "chef/event_dispatch/dsl" class Chef class << self diff --git a/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb b/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb index 269e160d43..b7c96c42e1 100644 --- a/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb +++ b/lib/chef/chef_fs/file_system/chef_server/versioned_cookbook_dir.rb @@ -24,7 +24,7 @@ class Chef module ChefServer class VersionedCookbookDir < CookbookDir # See Erchef code - # https://github.com/opscode/chef_objects/blob/968a63344d38fd507f6ace05f73d53e9cd7fb043/src/chef_regex.erl#L94 + # https://github.com/chef/chef_objects/blob/968a63344d38fd507f6ace05f73d53e9cd7fb043/src/chef_regex.erl#L94 VALID_VERSIONED_COOKBOOK_NAME = /^([.a-zA-Z0-9_-]+)-(\d+\.\d+\.\d+)$/ def initialize(name, parent, options = {}) diff --git a/lib/chef/dsl/core.rb b/lib/chef/dsl/core.rb index 11507857cf..d7c5b6a006 100644 --- a/lib/chef/dsl/core.rb +++ b/lib/chef/dsl/core.rb @@ -1,7 +1,7 @@ #-- # Author:: Adam Jacob (<adam@chef.io>) # Author:: Christopher Walters (<cw@chef.io>) -# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc. +# Copyright:: Copyright 2008-2016 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/lib/chef/dsl/declare_resource.rb b/lib/chef/dsl/declare_resource.rb index ed3083e1ca..5bd1f7fb8e 100644 --- a/lib/chef/dsl/declare_resource.rb +++ b/lib/chef/dsl/declare_resource.rb @@ -1,7 +1,7 @@ #-- # Author:: Adam Jacob (<adam@chef.io>) # Author:: Christopher Walters -# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc. +# Copyright:: Copyright 2008-2016 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -47,7 +47,7 @@ class Chef when Chef::RunContext rc when :root - Chef.run_context + run_context.root_run_context when :parent run_context.parent_run_context else diff --git a/lib/chef/dsl/method_missing.rb b/lib/chef/dsl/method_missing.rb index 0d7ded30f3..51c3eac606 100644 --- a/lib/chef/dsl/method_missing.rb +++ b/lib/chef/dsl/method_missing.rb @@ -1,5 +1,5 @@ #-- -# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc. +# Copyright:: Copyright 2008-2016 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb index 1bb8f130af..e2bd070179 100644 --- a/lib/chef/dsl/recipe.rb +++ b/lib/chef/dsl/recipe.rb @@ -1,7 +1,7 @@ #-- # Author:: Adam Jacob (<adam@chef.io>) # Author:: Christopher Walters (<cw@chef.io>) -# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc. +# Copyright:: Copyright 2008-2016 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/lib/chef/dsl/universal.rb b/lib/chef/dsl/universal.rb index 810792f542..6e3d162b6f 100644 --- a/lib/chef/dsl/universal.rb +++ b/lib/chef/dsl/universal.rb @@ -1,7 +1,7 @@ #-- # Author:: Adam Jacob (<adam@chef.io>) # Author:: Christopher Walters (<cw@chef.io>) -# Copyright:: Copyright 2008-2016, 2009-2015 Chef Software, Inc. +# Copyright:: Copyright 2008-2016 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/lib/chef/http.rb b/lib/chef/http.rb index 924081bc6b..12acae953c 100644 --- a/lib/chef/http.rb +++ b/lib/chef/http.rb @@ -5,7 +5,7 @@ # Author:: Christopher Brown (<cb@chef.io>) # Author:: Christopher Walters (<cw@chef.io>) # Author:: Daniel DeLeo (<dan@chef.io>) -# Copyright:: Copyright 2009-2016, 2013-2015 Chef Software, Inc. +# Copyright:: Copyright 2009-2016 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -329,10 +329,9 @@ class Chef # Runs a synchronous HTTP request, with no middleware applied (use #request # to have the middleware applied). The entire response will be loaded into memory. # @api private - def send_http_request(method, url, headers, body, &response_handler) - headers = build_headers(method, url, headers, body) - + def send_http_request(method, url, base_headers, body, &response_handler) retrying_http_errors(url) do + headers = build_headers(method, url, base_headers, body) client = http_client(url) return_value = nil if block_given? diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb index 0dbd02ceb4..8d40e06a43 100644 --- a/lib/chef/knife.rb +++ b/lib/chef/knife.rb @@ -408,11 +408,25 @@ class Chef config_loader = self.class.load_config(config[:config_file]) config[:config_file] = config_loader.config_location + # For CLI options like `--config-option key=value`. These have to get + # parsed and applied separately. + extra_config_options = config.delete(:config_option) + merge_configs apply_computed_config - Chef::Config.export_proxies + # This has to be after apply_computed_config so that Mixlib::Log is configured Chef::Log.info("Using configuration from #{config[:config_file]}") if config[:config_file] + + begin + Chef::Config.apply_extra_config_options(extra_config_options) + rescue ChefConfig::UnparsableConfigOption => e + ui.error e.message + show_usage + exit(1) + end + + Chef::Config.export_proxies end def show_usage @@ -525,7 +539,11 @@ class Chef # FIXME: yard with @yield def create_object(object, pretty_name = nil, object_class: nil) - output = edit_data(object, object_class: object_class) + output = if object_class + edit_data(object, object_class: object_class) + else + edit_hash(object) + end if Kernel.block_given? output = yield(output) diff --git a/lib/chef/mixin/powershell_out.rb b/lib/chef/mixin/powershell_out.rb index 74de85f86f..ab7cf00a72 100644 --- a/lib/chef/mixin/powershell_out.rb +++ b/lib/chef/mixin/powershell_out.rb @@ -91,7 +91,7 @@ class Chef "-InputFormat None", ] - "powershell.exe #{flags.join(' ')} -Command \"#{script}\"" + "powershell.exe #{flags.join(' ')} -Command \"#{script.gsub('"', '\"')}\"" end end end diff --git a/lib/chef/mixin/shell_out.rb b/lib/chef/mixin/shell_out.rb index a258a91075..d8607c8de7 100644 --- a/lib/chef/mixin/shell_out.rb +++ b/lib/chef/mixin/shell_out.rb @@ -61,7 +61,7 @@ class Chef [:command_log_prepend, :log_tag] ] # CHEF-3090: Deprecate command_log_level and command_log_prepend - # Patterned after https://github.com/opscode/chef/commit/e1509990b559984b43e428d4d801c394e970f432 + # Patterned after https://github.com/chef/chef/commit/e1509990b559984b43e428d4d801c394e970f432 def run_command_compatible_options(command_args) return command_args unless command_args.last.is_a?(Hash) diff --git a/lib/chef/node.rb b/lib/chef/node.rb index 212b1ced14..34a92d325b 100644 --- a/lib/chef/node.rb +++ b/lib/chef/node.rb @@ -197,7 +197,6 @@ class Chef # Set a normal attribute of this node, but auto-vivify any Mashes that # might be missing def normal - attributes.top_level_breadcrumb = nil attributes.normal end @@ -209,14 +208,12 @@ class Chef # Set a default of this node, but auto-vivify any Mashes that might # be missing def default - attributes.top_level_breadcrumb = nil attributes.default end # Set an override attribute of this node, but auto-vivify any Mashes that # might be missing def override - attributes.top_level_breadcrumb = nil attributes.override end @@ -237,7 +234,6 @@ class Chef end def automatic_attrs - attributes.top_level_breadcrumb = nil attributes.automatic end diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb index a4a07275c0..2d6aff0b21 100644 --- a/lib/chef/node/attribute.rb +++ b/lib/chef/node/attribute.rb @@ -17,6 +17,9 @@ # limitations under the License. # +require "chef/node/mixin/deep_merge_cache" +require "chef/node/mixin/immutablize_hash" +require "chef/node/mixin/state_tracking" require "chef/node/immutable_collections" require "chef/node/attribute_collections" require "chef/decorator/unchain" @@ -34,9 +37,18 @@ class Chef class Attribute < Mash include Immutablize - + # FIXME: what is include Enumerable doing up here, when down below we delegate + # most of the Enumerable/Hash things to the underlying merged ImmutableHash. That + # is, in fact, the correct, thing to do, while including Enumerable to try to create + # a hash-like API gets lots of things wrong because of the difference between the + # Hash `each do |key, value|` vs the Array-like `each do |value|` API that Enumerable + # expects. This include should probably be deleted? include Enumerable + include Chef::Node::Mixin::DeepMergeCache + include Chef::Node::Mixin::StateTracking + include Chef::Node::Mixin::ImmutablizeHash + # List of the component attribute hashes, in order of precedence, low to # high. COMPONENTS = [ @@ -175,39 +187,21 @@ class Chef # return the automatic level attribute component attr_reader :automatic - # This is used to track the top level key as we descend through method chaining into - # a precedence level (e.g. node.default['foo']['bar']['baz']= results in 'foo' here). We - # need this so that when we hit the end of a method chain which results in a mutator method - # that we can invalidate the whole top-level deep merge cache for the top-level key. It is - # the responsibility of the accessor on the Chef::Node object to reset this to nil, and then - # the first VividMash#[] call can ||= and set this to the first key we encounter. - attr_accessor :top_level_breadcrumb - - # Cache of deep merged values by top-level key. This is a simple hash which has keys that are the - # top-level keys of the node object, and we save the computed deep-merge for that key here. There is - # no cache of subtrees. - attr_accessor :deep_merge_cache - def initialize(normal, default, override, automatic) - @default = VividMash.new(self, default) - @env_default = VividMash.new(self, {}) - @role_default = VividMash.new(self, {}) - @force_default = VividMash.new(self, {}) - - @normal = VividMash.new(self, normal) + @default = VividMash.new(default, self) + @env_default = VividMash.new({}, self) + @role_default = VividMash.new({}, self) + @force_default = VividMash.new({}, self) - @override = VividMash.new(self, override) - @role_override = VividMash.new(self, {}) - @env_override = VividMash.new(self, {}) - @force_override = VividMash.new(self, {}) + @normal = VividMash.new(normal, self) - @automatic = VividMash.new(self, automatic) + @override = VividMash.new(override, self) + @role_override = VividMash.new({}, self) + @env_override = VividMash.new({}, self) + @force_override = VividMash.new({}, self) - @merged_attributes = nil - @combined_override = nil - @combined_default = nil - @top_level_breadcrumb = nil - @deep_merge_cache = {} + @automatic = VividMash.new(automatic, self) + super() end # Debug what's going on with an attribute. +args+ is a path spec to the @@ -235,76 +229,62 @@ class Chef end end - # Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate - # the entire deep_merge cache. In the case of the user doing node.default['foo']['bar']['baz']= - # that eventually results in a call to reset_cache('foo') here. A node.default=hash_thing call - # must invalidate the entire cache and re-deep-merge the entire node object. - def reset_cache(path = nil) - if path.nil? - @deep_merge_cache = {} - else - deep_merge_cache.delete(path.to_s) - end - end - - alias :reset :reset_cache - # Set the cookbook level default attribute component to +new_data+. def default=(new_data) reset - @default = VividMash.new(self, new_data) + @default = VividMash.new(new_data, self) end # Set the role level default attribute component to +new_data+ def role_default=(new_data) reset - @role_default = VividMash.new(self, new_data) + @role_default = VividMash.new(new_data, self) end # Set the environment level default attribute component to +new_data+ def env_default=(new_data) reset - @env_default = VividMash.new(self, new_data) + @env_default = VividMash.new(new_data, self) end # Set the force_default (+default!+) level attributes to +new_data+ def force_default=(new_data) reset - @force_default = VividMash.new(self, new_data) + @force_default = VividMash.new(new_data, self) end # Set the normal level attribute component to +new_data+ def normal=(new_data) reset - @normal = VividMash.new(self, new_data) + @normal = VividMash.new(new_data, self) end # Set the cookbook level override attribute component to +new_data+ def override=(new_data) reset - @override = VividMash.new(self, new_data) + @override = VividMash.new(new_data, self) end # Set the role level override attribute component to +new_data+ def role_override=(new_data) reset - @role_override = VividMash.new(self, new_data) + @role_override = VividMash.new(new_data, self) end # Set the environment level override attribute component to +new_data+ def env_override=(new_data) reset - @env_override = VividMash.new(self, new_data) + @env_override = VividMash.new(new_data, self) end def force_override=(new_data) reset - @force_override = VividMash.new(self, new_data) + @force_override = VividMash.new(new_data, self) end def automatic=(new_data) reset - @automatic = VividMash.new(self, new_data) + @automatic = VividMash.new(new_data, self) end # @@ -413,36 +393,6 @@ class Chef write(:force_override, *args, value) end - # method-style access to attributes - - def read(*path) - merged_attributes.read(*path) - end - - def read!(*path) - merged_attributes.read!(*path) - end - - def exist?(*path) - merged_attributes.exist?(*path) - end - - def write(level, *args, &block) - self.send(level).write(*args, &block) - end - - def write!(level, *args, &block) - self.send(level).write!(*args, &block) - end - - def unlink(level, *path) - self.send(level).unlink(*path) - end - - def unlink!(level, *path) - self.send(level).unlink!(*path) - end - # # Accessing merged attributes. # @@ -484,24 +434,39 @@ class Chef write(:normal, *args) if read(*args[0...-1]).nil? end - def [](key) - if deep_merge_cache.has_key?(key.to_s) - # return the cache of the deep merged values by top-level key - deep_merge_cache[key.to_s] - else - # save all the work of computing node[key] - deep_merge_cache[key.to_s] = merged_attributes(key) + def has_key?(key) + COMPONENTS.any? do |component_ivar| + instance_variable_get(component_ivar).has_key?(key) end end + # method-style access to attributes (has to come after the prepended ImmutablizeHash) - def []=(key, value) - raise Exceptions::ImmutableAttributeModification + def read(*path) + merged_attributes.read(*path) end - def has_key?(key) - COMPONENTS.any? do |component_ivar| - instance_variable_get(component_ivar).has_key?(key) - end + def read!(*path) + merged_attributes.read!(*path) + end + + def exist?(*path) + merged_attributes.exist?(*path) + end + + def write(level, *args, &block) + self.send(level).write(*args, &block) + end + + def write!(level, *args, &block) + self.send(level).write!(*args, &block) + end + + def unlink(level, *path) + self.send(level).unlink(*path) + end + + def unlink!(level, *path) + self.send(level).unlink!(*path) end alias :attribute? :has_key? @@ -600,7 +565,7 @@ class Chef return nil if components.compact.empty? - components.inject(ImmutableMash.new({})) do |merged, component| + components.inject(ImmutableMash.new({}, self)) do |merged, component| Chef::Mixin::DeepMerge.hash_only_merge!(merged, component) end end @@ -631,6 +596,11 @@ class Chef end end + # needed for __path__ + def convert_key(key) + key.kind_of?(Symbol) ? key.to_s : key + end + end end diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb index 1bd31bceb0..b01b447978 100644 --- a/lib/chef/node/attribute_collections.rb +++ b/lib/chef/node/attribute_collections.rb @@ -17,6 +17,7 @@ # require "chef/node/common_api" +require "chef/node/mixin/state_tracking" class Chef class Node @@ -63,15 +64,12 @@ class Chef MUTATOR_METHODS.each do |mutator| define_method(mutator) do |*args, &block| ret = super(*args, &block) - root.reset_cache(root.top_level_breadcrumb) + send_reset_cache ret end end - attr_reader :root - - def initialize(root, data) - @root = root + def initialize(data = []) super(data) map! { |e| convert_value(e) } end @@ -96,14 +94,20 @@ class Chef when AttrArray value when Hash - VividMash.new(root, value) + VividMash.new(value, __root__) when Array - AttrArray.new(root, value) + AttrArray.new(value, __root__) else value end end + # needed for __path__ + def convert_key(key) + key + end + + prepend Chef::Node::Mixin::StateTracking end # == VividMash @@ -117,8 +121,6 @@ class Chef # #fetch, work as normal). # * attr_accessor style element set and get are supported via method_missing class VividMash < Mash - attr_reader :root - include CommonAPI # Methods that mutate a VividMash. Each of them is overridden so that it @@ -126,7 +128,6 @@ class Chef # object. MUTATOR_METHODS = [ :clear, - :delete, :delete_if, :keep_if, :merge!, @@ -140,23 +141,27 @@ class Chef # For all of the mutating methods on Mash, override them so that they # also invalidate the cached `merged_attributes` on the root Attribute # object. + + def delete(key, &block) + send_reset_cache(__path__ + [ key ]) + super + end + MUTATOR_METHODS.each do |mutator| define_method(mutator) do |*args, &block| - root.reset_cache(root.top_level_breadcrumb) + send_reset_cache super(*args, &block) end end - def initialize(root, data = {}) - @root = root + def initialize(data = {}) super(data) end def [](key) - root.top_level_breadcrumb ||= key value = super if !key?(key) - value = self.class.new(root) + value = self.class.new({}, __root__) self[key] = value else value @@ -164,9 +169,8 @@ class Chef end def []=(key, value) - root.top_level_breadcrumb ||= key ret = super - root.reset_cache(root.top_level_breadcrumb) + send_reset_cache(__path__ + [ key ]) ret end @@ -205,9 +209,9 @@ class Chef when AttrArray value when Hash - VividMash.new(root, value) + VividMash.new(value, __root__) when Array - AttrArray.new(root, value) + AttrArray.new(value, __root__) else value end @@ -217,6 +221,7 @@ class Chef Mash.new(self) end + prepend Chef::Node::Mixin::StateTracking end end end diff --git a/lib/chef/node/common_api.rb b/lib/chef/node/common_api.rb index ce2c6b6878..9bb83a5178 100644 --- a/lib/chef/node/common_api.rb +++ b/lib/chef/node/common_api.rb @@ -32,7 +32,6 @@ class Chef # - autovivifying / autoreplacing writer # - non-container-ey intermediate objects are replaced with hashes def write(*args, &block) - root.top_level_breadcrumb = nil if respond_to?(:root) value = block_given? ? yield : args.pop last = args.pop prev_memo = prev_key = nil @@ -56,7 +55,6 @@ class Chef # something that is not a container ("schema violation" issues). # def write!(*args, &block) - root.top_level_breadcrumb = nil if respond_to?(:root) value = block_given? ? yield : args.pop last = args.pop obj = args.inject(self) do |memo, key| @@ -71,7 +69,6 @@ class Chef # return true or false based on if the attribute exists def exist?(*path) - root.top_level_breadcrumb = nil if respond_to?(:root) path.inject(self) do |memo, key| return false unless valid_container?(memo, key) if memo.is_a?(Hash) @@ -103,7 +100,6 @@ class Chef # non-autovivifying reader that throws an exception if the attribute does not exist def read!(*path) raise Chef::Exceptions::NoSuchAttribute unless exist?(*path) - root.top_level_breadcrumb = nil if respond_to?(:root) path.inject(self) do |memo, key| memo[key] end @@ -112,10 +108,8 @@ class Chef # FIXME:(?) does anyone really like the autovivifying reader that we have and wants the same behavior? readers that write? ugh... def unlink(*path, last) - root.top_level_breadcrumb = nil if respond_to?(:root) hash = path.empty? ? self : read(*path) return nil unless hash.is_a?(Hash) || hash.is_a?(Array) - root.top_level_breadcrumb ||= last hash.delete(last) end diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb index d4623ace2a..938135cbee 100644 --- a/lib/chef/node/immutable_collections.rb +++ b/lib/chef/node/immutable_collections.rb @@ -16,6 +16,9 @@ # require "chef/node/common_api" +require "chef/node/mixin/state_tracking" +require "chef/node/mixin/immutablize_array" +require "chef/node/mixin/immutablize_hash" class Chef class Node @@ -24,9 +27,9 @@ class Chef def immutablize(value) case value when Hash - ImmutableMash.new(value) + ImmutableMash.new(value, __root__) when Array - ImmutableArray.new(value) + ImmutableArray.new(value, __root__) else value end @@ -49,55 +52,12 @@ class Chef alias :internal_push :<< private :internal_push - # A list of methods that mutate Array. Each of these is overridden to - # raise an error, making this instances of this class more or less - # immutable. - DISALLOWED_MUTATOR_METHODS = [ - :<<, - :[]=, - :clear, - :collect!, - :compact!, - :default=, - :default_proc=, - :delete, - :delete_at, - :delete_if, - :fill, - :flatten!, - :insert, - :keep_if, - :map!, - :merge!, - :pop, - :push, - :update, - :reject!, - :reverse!, - :replace, - :select!, - :shift, - :slice!, - :sort!, - :sort_by!, - :uniq!, - :unshift, - ] - - def initialize(array_data) + def initialize(array_data = []) array_data.each do |value| internal_push(immutablize(value)) end end - # Redefine all of the methods that mutate a Hash to raise an error when called. - # This is the magic that makes this object "Immutable" - DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name| - define_method(mutator_method_name) do |*args, &block| - raise Exceptions::ImmutableAttributeModification - end - end - # For elements like Fixnums, true, nil... def safe_dup(e) e.dup @@ -125,6 +85,13 @@ class Chef a end + # for consistency's sake -- integers 'converted' to integers + def convert_key(key) + key + end + + prepend Chef::Node::Mixin::StateTracking + prepend Chef::Node::Mixin::ImmutablizeArray end # == ImmutableMash @@ -140,36 +107,13 @@ class Chef # it is stale. # * Values can be accessed in attr_reader-like fashion via method_missing. class ImmutableMash < Mash - include Immutablize include CommonAPI alias :internal_set :[]= private :internal_set - DISALLOWED_MUTATOR_METHODS = [ - :[]=, - :clear, - :collect!, - :default=, - :default_proc=, - :delete, - :delete_if, - :keep_if, - :map!, - :merge!, - :update, - :reject!, - :replace, - :select!, - :shift, - :write, - :write!, - :unlink, - :unlink!, - ] - - def initialize(mash_data) + def initialize(mash_data = {}) mash_data.each do |key, value| internal_set(key, immutablize(value)) end @@ -181,14 +125,6 @@ class Chef alias :attribute? :has_key? - # Redefine all of the methods that mutate a Hash to raise an error when called. - # This is the magic that makes this object "Immutable" - DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name| - define_method(mutator_method_name) do |*args, &block| - raise Exceptions::ImmutableAttributeModification - end - end - def method_missing(symbol, *args) if symbol == :to_ary super @@ -238,7 +174,8 @@ class Chef h end + prepend Chef::Node::Mixin::StateTracking + prepend Chef::Node::Mixin::ImmutablizeHash end - end end diff --git a/lib/chef/node/mixin/deep_merge_cache.rb b/lib/chef/node/mixin/deep_merge_cache.rb new file mode 100644 index 0000000000..b18d6b10cb --- /dev/null +++ b/lib/chef/node/mixin/deep_merge_cache.rb @@ -0,0 +1,61 @@ +#-- +# Copyright:: Copyright 2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef + class Node + module Mixin + module DeepMergeCache + # Cache of deep merged values by top-level key. This is a simple hash which has keys that are the + # top-level keys of the node object, and we save the computed deep-merge for that key here. There is + # no cache of subtrees. + attr_accessor :deep_merge_cache + + def initialize + @merged_attributes = nil + @combined_override = nil + @combined_default = nil + @deep_merge_cache = {} + end + + # Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate + # the entire deep_merge cache. In the case of the user doing node.default['foo']['bar']['baz']= + # that eventually results in a call to reset_cache('foo') here. A node.default=hash_thing call + # must invalidate the entire cache and re-deep-merge the entire node object. + def reset_cache(path = nil) + if path.nil? + deep_merge_cache.clear + else + deep_merge_cache.delete(path.to_s) + end + end + + alias :reset :reset_cache + + def [](key) + if deep_merge_cache.has_key?(key.to_s) + # return the cache of the deep merged values by top-level key + deep_merge_cache[key.to_s] + else + # save all the work of computing node[key] + deep_merge_cache[key.to_s] = merged_attributes(key) + end + end + + end + end + end +end diff --git a/lib/chef/node/mixin/immutablize_array.rb b/lib/chef/node/mixin/immutablize_array.rb new file mode 100644 index 0000000000..cfa7266b9a --- /dev/null +++ b/lib/chef/node/mixin/immutablize_array.rb @@ -0,0 +1,67 @@ +#-- +# Copyright:: Copyright 2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef + class Node + module Mixin + module ImmutablizeArray + # A list of methods that mutate Array. Each of these is overridden to + # raise an error, making this instances of this class more or less + # immutable. + DISALLOWED_MUTATOR_METHODS = [ + :<<, + :[]=, + :clear, + :collect!, + :compact!, + :default=, + :default_proc=, + :delete, + :delete_at, + :delete_if, + :fill, + :flatten!, + :insert, + :keep_if, + :map!, + :merge!, + :pop, + :push, + :update, + :reject!, + :reverse!, + :replace, + :select!, + :shift, + :slice!, + :sort!, + :sort_by!, + :uniq!, + :unshift, + ] + + # Redefine all of the methods that mutate a Hash to raise an error when called. + # This is the magic that makes this object "Immutable" + DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name| + define_method(mutator_method_name) do |*args, &block| + raise Exceptions::ImmutableAttributeModification + end + end + end + end + end +end diff --git a/lib/chef/node/mixin/immutablize_hash.rb b/lib/chef/node/mixin/immutablize_hash.rb new file mode 100644 index 0000000000..f09e6944fc --- /dev/null +++ b/lib/chef/node/mixin/immutablize_hash.rb @@ -0,0 +1,54 @@ +#-- +# Copyright:: Copyright 2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef + class Node + module Mixin + module ImmutablizeHash + DISALLOWED_MUTATOR_METHODS = [ + :[]=, + :clear, + :collect!, + :default=, + :default_proc=, + :delete, + :delete_if, + :keep_if, + :map!, + :merge!, + :update, + :reject!, + :replace, + :select!, + :shift, + :write, + :write!, + :unlink, + :unlink!, + ] + + # Redefine all of the methods that mutate a Hash to raise an error when called. + # This is the magic that makes this object "Immutable" + DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name| + define_method(mutator_method_name) do |*args, &block| + raise Exceptions::ImmutableAttributeModification + end + end + end + end + end +end diff --git a/lib/chef/node/mixin/state_tracking.rb b/lib/chef/node/mixin/state_tracking.rb new file mode 100644 index 0000000000..9be102eeeb --- /dev/null +++ b/lib/chef/node/mixin/state_tracking.rb @@ -0,0 +1,71 @@ +#-- +# Copyright:: Copyright 2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef + class Node + module Mixin + module StateTracking + attr_reader :__path__ + attr_reader :__root__ + + NULL = Object.new + + def initialize(data = NULL, root = self) + # __path__ and __root__ must be nil when we call super so it knows + # to avoid resetting the cache on construction + data == NULL ? super() : super(data) + @__path__ = [] + @__root__ = root + end + + def [](key) + ret = super + if ret.is_a?(StateTracking) + ret.__path__ = __path__ + [ convert_key(key) ] + ret.__root__ = __root__ + end + ret + end + + def []=(key, value) + ret = super + if ret.is_a?(StateTracking) + ret.__path__ = __path__ + [ convert_key(key) ] + ret.__root__ = __root__ + end + ret + end + + protected + + def __path__=(path) + @__path__ = path + end + + def __root__=(root) + @__root__ = root + end + + private + + def send_reset_cache(path = __path__) + __root__.reset_cache(path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !path.nil? + end + end + end + end +end diff --git a/lib/chef/provider/cron.rb b/lib/chef/provider/cron.rb index 7baaeec0c5..35e5bb874c 100644 --- a/lib/chef/provider/cron.rb +++ b/lib/chef/provider/cron.rb @@ -199,7 +199,7 @@ class Chef def set_environment_var(attr_name, attr_value) if %w{MAILTO PATH SHELL HOME}.include?(attr_name) - @current_resource.send(attr_name.downcase.to_sym, attr_value) + @current_resource.send(attr_name.downcase.to_sym, attr_value.gsub(/^"|"$/, "")) else @current_resource.environment(@current_resource.environment.merge(attr_name => attr_value)) end diff --git a/lib/chef/provider/group/suse.rb b/lib/chef/provider/group/suse.rb index a79038e25e..71336f9311 100644 --- a/lib/chef/provider/group/suse.rb +++ b/lib/chef/provider/group/suse.rb @@ -17,6 +17,7 @@ # require "chef/provider/group/groupadd" +require "etc" class Chef class Provider @@ -36,24 +37,42 @@ class Chef a.failure_message Chef::Exceptions::Group, "Could not find binary /usr/sbin/groupmod for #{@new_resource.name}" # No whyrun alternative: this component should be available in the base install of any given system that uses it end + + requirements.assert(:create, :manage, :modify) do |a| + a.assertion do + begin + to_add(@new_resource.members).all? { |member| Etc.getpwnam(member) } + rescue + false + end + end + a.failure_message Chef::Exceptions::Group, "Could not add users #{to_add(@new_resource.members).join(", ")} to #{@new_resource.group_name}: one of these users does not exist" + a.whyrun "Could not find one of these users: #{to_add(@new_resource.members).join(", ")}. Assuming it will be created by a prior step" + end end def set_members(members) - to_delete = @current_resource.members - members - to_delete.each do |member| + to_remove(members).each do |member| remove_member(member) end - to_add = members - @current_resource.members - to_add.each do |member| + to_add(members).each do |member| add_member(member) end end + def to_add(members) + members - @current_resource.members + end + def add_member(member) shell_out!("groupmod -A #{member} #{@new_resource.group_name}") end + def to_remove(members) + @current_resource.members - members + end + def remove_member(member) shell_out!("groupmod -R #{member} #{@new_resource.group_name}") end diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb index 3f641145e6..3fed63c914 100644 --- a/lib/chef/provider/package.rb +++ b/lib/chef/provider/package.rb @@ -31,6 +31,8 @@ class Chef include Chef::Mixin::ShellOut extend Chef::Mixin::SubclassDirective + use_inline_resources + # subclasses declare this if they want all their arguments as arrays of packages and names subclass_directive :use_multipackage_api # subclasses declare this if they want sources (filenames) pulled from their package names @@ -81,7 +83,7 @@ class Chef end end - def action_install + action :install do unless target_version_array.any? Chef::Log.debug("#{@new_resource} is already installed - nothing to do") return @@ -116,7 +118,7 @@ class Chef private :install_description - def action_upgrade + action :upgrade do if !target_version_array.any? Chef::Log.debug("#{@new_resource} no versions to upgrade - nothing to do") return @@ -146,7 +148,7 @@ class Chef private :upgrade_description - def action_remove + action :remove do if removing_package? description = @new_resource.version ? "version #{@new_resource.version} of " : "" converge_by("remove #{description}package #{@current_resource.package_name}") do @@ -181,7 +183,7 @@ class Chef end end - def action_purge + action :purge do if removing_package? description = @new_resource.version ? "version #{@new_resource.version} of" : "" converge_by("purge #{description} package #{@current_resource.package_name}") do @@ -193,7 +195,7 @@ class Chef end end - def action_reconfig + action :reconfig do if @current_resource.version == nil Chef::Log.debug("#{@new_resource} is NOT installed - nothing to do") return diff --git a/lib/chef/provider/package/windows/exe.rb b/lib/chef/provider/package/windows/exe.rb index 44a2f19d1e..60065d9019 100644 --- a/lib/chef/provider/package/windows/exe.rb +++ b/lib/chef/provider/package/windows/exe.rb @@ -89,9 +89,10 @@ class Chef end def current_installed_version - @current_installed_version ||= uninstall_entries.count == 0 ? nil : begin - uninstall_entries.map { |entry| entry.display_version }.uniq - end + @current_installed_version ||= + if uninstall_entries.count != 0 + uninstall_entries.map { |entry| entry.display_version }.uniq + end end # http://unattended.sourceforge.net/installers.php diff --git a/lib/chef/provider/package/windows/msi.rb b/lib/chef/provider/package/windows/msi.rb index 301baa4ed5..ee3b2f7e8e 100644 --- a/lib/chef/provider/package/windows/msi.rb +++ b/lib/chef/provider/package/windows/msi.rb @@ -50,7 +50,7 @@ class Chef Chef::Log.debug("#{new_resource} checking package status and version for #{product_code}") get_installed_version(product_code) else - uninstall_entries.count == 0 ? nil : begin + if uninstall_entries.count != 0 uninstall_entries.map { |entry| entry.display_version }.uniq end end diff --git a/lib/chef/provider/ruby_block.rb b/lib/chef/provider/ruby_block.rb index 0817b14044..18ee9cecc5 100644 --- a/lib/chef/provider/ruby_block.rb +++ b/lib/chef/provider/ruby_block.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob (<adam@chef.io>) # Author:: AJ Christensen (<aj@chef.io>) -# Copyright:: Copyright 2009-2016, Opscode +# Copyright:: Copyright 2009-2016, Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb index f6b088d333..4b05ac8f5e 100644 --- a/lib/chef/provider/user.rb +++ b/lib/chef/provider/user.rb @@ -113,15 +113,13 @@ class Chef # <true>:: If a change is required # <false>:: If the users are identical def compare_user - changed = [ :comment, :home, :shell, :password ].select do |user_attrib| - !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib) != @current_resource.send(user_attrib) - end + return true if !@new_resource.home.nil? && Pathname.new(@new_resource.home).cleanpath != Pathname.new(@current_resource.home).cleanpath - changed += [ :uid, :gid ].select do |user_attrib| - !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib).to_s != @current_resource.send(user_attrib).to_s + [ :comment, :shell, :password, :uid, :gid ].each do |user_attrib| + return true if !@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib).to_s != @current_resource.send(user_attrib).to_s end - changed.any? + false end def action_create diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index 8048330ce1..074ab772a1 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -186,22 +186,16 @@ class Chef # This should most likely be paired with action :nothing # # @param arg [Array[Symbol], Symbol] A list of actions (e.g. `:create`) - # @return [Array[Symbol]] the list of actions. # - def delayed_action(arg = nil) - if arg - arg = Array(arg).map(&:to_sym) - arg.each do |action| - validate( - { action: action }, - { action: { kind_of: Symbol, equal_to: allowed_actions } } - ) - # the resource effectively sends a delayed notification to itself - run_context.add_delayed_action(Notification.new(self, action, self)) - end - @delayed_action = arg - else - @delayed_action + def delayed_action(arg) + arg = Array(arg).map(&:to_sym) + arg.map do |action| + validate( + { action: action }, + { action: { kind_of: Symbol, equal_to: allowed_actions } } + ) + # the resource effectively sends a delayed notification to itself + run_context.add_delayed_action(Notification.new(self, action, self)) end end diff --git a/lib/chef/resource/file.rb b/lib/chef/resource/file.rb index 207de63778..5c275a574f 100644 --- a/lib/chef/resource/file.rb +++ b/lib/chef/resource/file.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob (<adam@chef.io>) # Author:: Seth Chisamore (<schisamo@chef.io>) -# Copyright:: Copyright 2008-2016, 2011-2015 Chef Software, Inc. +# Copyright:: Copyright 2008-2016 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/lib/chef/resource/scm.rb b/lib/chef/resource/scm.rb index 1e8c71e59d..533723c2c4 100644 --- a/lib/chef/resource/scm.rb +++ b/lib/chef/resource/scm.rb @@ -89,13 +89,7 @@ class Chef ) end - def svn_password(arg = nil) - set_or_return( - :svn_password, - arg, - :kind_of => String - ) - end + property :svn_password, String, sensitive: true, desired_state: false def svn_arguments(arg = nil) @svn_arguments, arg = nil, nil if arg == false diff --git a/lib/chef/resource/yum_repository.rb b/lib/chef/resource/yum_repository.rb index b1859361b1..1c215b51ff 100644 --- a/lib/chef/resource/yum_repository.rb +++ b/lib/chef/resource/yum_repository.rb @@ -57,7 +57,7 @@ class Chef property :password, String, regex: /.*/ property :repo_gpgcheck, [TrueClass, FalseClass] property :report_instanceid, [TrueClass, FalseClass] - property :repositoryid, String, regex: /.*/, name_attribute: true + property :repositoryid, String, regex: /.*/, name_property: true property :sensitive, [TrueClass, FalseClass], default: false property :skip_if_unavailable, [TrueClass, FalseClass] property :source, String, regex: /.*/ diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb index 5d29f766c9..626977b09f 100644 --- a/lib/chef/run_context.rb +++ b/lib/chef/run_context.rb @@ -85,6 +85,17 @@ class Chef attr_reader :parent_run_context # + # The root run context. + # + # @return [Chef::RunContext] The root run context. + # + def root_run_context + rc = self + rc = rc.parent_run_context until rc.parent_run_context.nil? + rc + end + + # # The collection of resources intended to be converged (and able to be # notified). # @@ -653,6 +664,7 @@ ERROR_MESSAGE notifies_immediately notifies_delayed parent_run_context + root_run_context resource_collection resource_collection= }.map { |x| x.to_sym } diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 078d42c305..3413b702c4 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -21,7 +21,7 @@ class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.15.26" + VERSION = "12.16.23" end # diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index a54921cb4b..182b723d22 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: git://github.com/chef/license_scout.git - revision: 4fd26d99d0617d6336ae372f026c190617800e6b + revision: ed2fe16bff7b1b337c3ae23fee8d63cce018303c specs: license_scout (0.1.2) ffi-yajl (~> 2.2) @@ -8,7 +8,7 @@ GIT GIT remote: git://github.com/chef/omnibus-software.git - revision: 4ce91af05b8be7d46feac765d0fc24eb28e62c68 + revision: cd69f8563fa8875c82a4e44652ffbfdac7ef201d branch: shain/ruby_windows_monster specs: omnibus-software (4.0.0) @@ -17,7 +17,7 @@ GIT GIT remote: git://github.com/chef/omnibus.git - revision: 1b5c7a20da006403d5aac2ddc754e43fb97cb540 + revision: d8a531fd4b95fc73c5c279ba0a53cedd6b2a1196 branch: rhass/COOL-502_with_gcc_investigate specs: omnibus (5.5.0) @@ -38,12 +38,12 @@ GEM addressable (2.4.0) artifactory (2.5.0) awesome_print (1.7.0) - aws-sdk (2.6.5) - aws-sdk-resources (= 2.6.5) - aws-sdk-core (2.6.5) + aws-sdk (2.6.11) + aws-sdk-resources (= 2.6.11) + aws-sdk-core (2.6.11) jmespath (~> 1.0) - aws-sdk-resources (2.6.5) - aws-sdk-core (= 2.6.5) + aws-sdk-resources (2.6.11) + aws-sdk-core (= 2.6.11) berkshelf (4.3.5) addressable (~> 2.3, >= 2.3.4) berkshelf-api-client (~> 2.0, >= 2.0.2) @@ -83,7 +83,7 @@ GEM celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.14.89) + chef-config (12.15.19) addressable fuzzyurl mixlib-config (~> 2.0) @@ -127,10 +127,11 @@ GEM mixlib-log mixlib-cli (1.7.0) mixlib-config (2.2.4) - mixlib-install (2.0.3) + mixlib-install (2.1.1) artifactory mixlib-shellout mixlib-versioning + thor mixlib-log (1.7.1) mixlib-shellout (2.2.7) mixlib-shellout (2.2.7-universal-mingw32) @@ -149,7 +150,7 @@ GEM nori (2.6.0) octokit (4.3.0) sawyer (~> 0.7.0, >= 0.5.3) - ohai (8.20.0) + ohai (8.21.0) chef-config (>= 12.5.0.alpha.1, < 13) ffi (~> 1.9) ffi-yajl (~> 2.2) @@ -227,7 +228,7 @@ GEM hashie (>= 2.0.2, < 4.0.0) win32-process (0.8.3) ffi (>= 1.0.0) - winrm (2.0.3) + winrm (2.1.0) builder (>= 2.1.2) erubis (~> 2.7) gssapi (~> 1.2) diff --git a/omnibus/README.md b/omnibus/README.md index 98a9094325..5f325aa164 100644 --- a/omnibus/README.md +++ b/omnibus/README.md @@ -75,7 +75,7 @@ changing the list found in the `.kitchen.yml` `platforms` YAML stanza. This build environment is designed to get you up-and-running quickly. However, there is nothing that restricts you to building on other platforms. Simply use -the [omnibus cookbook](https://github.com/opscode-cookbooks/omnibus) to setup +the [omnibus cookbook](https://github.com/chef-cookbooks/omnibus) to setup your desired platform and execute the build steps listed above. The default build environment requires Test Kitchen and VirtualBox for local diff --git a/omnibus_overrides.rb b/omnibus_overrides.rb index 53946131cd..24084dd6ba 100644 --- a/omnibus_overrides.rb +++ b/omnibus_overrides.rb @@ -16,4 +16,4 @@ override "ruby-windows-devkit-bash", version: "3.1.23-4-msys-1.0.18" override "util-macros", version: "1.19.0" override "xproto", version: "7.0.28" override "zlib", version: "1.2.8" -override "openssl", version: "1.0.2h" +override "openssl", version: "1.0.2j" diff --git a/spec/functional/resource/group_spec.rb b/spec/functional/resource/group_spec.rb index 31f9933546..7effd386a4 100644 --- a/spec/functional/resource/group_spec.rb +++ b/spec/functional/resource/group_spec.rb @@ -425,6 +425,7 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end end it "does not raise an error on manage" do + allow(Etc).to receive(:getpwnam).and_return(double("User")) expect { group_resource.run_action(:manage) }.not_to raise_error end end diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb index 79d62436f5..874155c107 100644 --- a/spec/functional/resource/user/useradd_spec.rb +++ b/spec/functional/resource/user/useradd_spec.rb @@ -637,8 +637,10 @@ describe Chef::Provider::User::Useradd, metadata do context "and has no password" do # TODO: platform_family should be setup in spec_helper w/ tags - if %w{suse opensuse}.include?(OHAI_SYSTEM["platform_family"]) - # suse gets this right: + if %w{opensuse}.include?(OHAI_SYSTEM["platform_family"]) || + (%w{suse}.include?(OHAI_SYSTEM["platform_family"]) && + OHAI_SYSTEM["platform_version"].to_f < 12.1) + # suse 11.x gets this right: it "errors out trying to unlock the user" do expect(@error).to be_a(Mixlib::ShellOut::ShellCommandFailed) expect(@error.message).to include("Cannot unlock the password") diff --git a/spec/integration/knife/data_bag_create_spec.rb b/spec/integration/knife/data_bag_create_spec.rb index 0a07792dbc..dc61d55fd5 100644 --- a/spec/integration/knife/data_bag_create_spec.rb +++ b/spec/integration/knife/data_bag_create_spec.rb @@ -34,12 +34,10 @@ describe "knife data bag create", :workstation do end it "creates a new data bag and item" do - pending "Deprecation warning must get fixed" knife("data bag create foo bar").should_succeed stdout: out, stderr: err end it "adds a new item to an existing bag" do - pending "Deprecation warning must get fixed" knife("data bag create foo").should_succeed stderr: err knife("data bag create foo bar").should_succeed stdout: out, stderr: exists end @@ -50,7 +48,6 @@ describe "knife data bag create", :workstation do end it "fails to add an existing item" do - pending "Deprecation warning must get fixed" knife("data bag create foo bar").should_succeed stdout: out, stderr: err expect { knife("data bag create foo bar") }.to raise_error(Net::HTTPServerException) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 47a5ec7f9f..7559e797bc 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -174,13 +174,13 @@ RSpec.configure do |config| running_platform_arch = `uname -m`.strip unless windows? - config.filter_run_excluding :arch => lambda {|target_arch| + config.filter_run_excluding :arch => lambda { |target_arch| running_platform_arch != target_arch } # Functional Resource tests that are provider-specific: # context "on platforms that use useradd", :provider => {:user => Chef::Provider::User::Useradd}} do #... - config.filter_run_excluding :provider => lambda {|criteria| + config.filter_run_excluding :provider => lambda { |criteria| type, target_provider = criteria.first node = TEST_NODE.dup diff --git a/spec/support/shared/integration/app_server_support.rb b/spec/support/shared/integration/app_server_support.rb index 4dfa3fa155..e2bb3812ea 100644 --- a/spec/support/shared/integration/app_server_support.rb +++ b/spec/support/shared/integration/app_server_support.rb @@ -1,7 +1,7 @@ # # Author:: John Keiser (<jkeiser@chef.io>) # Author:: Ho-Sheng Hsiao (<hosh@chef.io>) -# Copyright:: Copyright 2012-2016, 2013-2015 Chef Software, Inc. +# Copyright:: Copyright 2012-2016 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/spec/unit/http_spec.rb b/spec/unit/http_spec.rb index d99f03aac9..d58f07c417 100644 --- a/spec/unit/http_spec.rb +++ b/spec/unit/http_spec.rb @@ -74,7 +74,7 @@ describe Chef::HTTP do expect(http.create_url("///api/endpoint?url=http://foo.bar")).to eql(URI.parse("http://www.getchef.com/organization/org/api/endpoint?url=http://foo.bar")) end - # As per: https://github.com/opscode/chef/issues/2500 + # As per: https://github.com/chef/chef/issues/2500 it "should treat scheme part of the URI in a case-insensitive manner" do http = Chef::HTTP.allocate # Calling Chef::HTTP::new sets @url, don't want that. expect { http.create_url("HTTP://www1.chef.io/") }.not_to raise_error diff --git a/spec/unit/knife/core/gem_glob_loader_spec.rb b/spec/unit/knife/core/gem_glob_loader_spec.rb index 69a40ebaed..2f9e04769e 100644 --- a/spec/unit/knife/core/gem_glob_loader_spec.rb +++ b/spec/unit/knife/core/gem_glob_loader_spec.rb @@ -78,7 +78,7 @@ describe Chef::Knife::SubcommandLoader::GemGlobLoader do expect(loader.site_subcommands).to include(expected_command) end - # https://github.com/opscode/chef-dk/issues/227 + # https://github.com/chef/chef-dk/issues/227 # # `knife` in ChefDK isn't from a gem install, it's directly run from a clone # of the source, but there can be one or more versions of chef also installed diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb index f0ec45d59a..9569526b2a 100644 --- a/spec/unit/knife_spec.rb +++ b/spec/unit/knife_spec.rb @@ -349,6 +349,37 @@ describe Chef::Knife do expect { knife.run_with_pretty_exceptions }.to raise_error(Exception) end end + + describe "setting arbitrary configuration with --config-option" do + + let(:stdout) { StringIO.new } + + let(:stderr) { StringIO.new } + + let(:stdin) { StringIO.new } + + let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, disable_editing: true) } + + let(:subcommand) do + KnifeSpecs::TestYourself.options = Chef::Application::Knife.options.merge(KnifeSpecs::TestYourself.options) + KnifeSpecs::TestYourself.new(%w{--config-option badly_formatted_arg}).tap do |cmd| + cmd.ui = ui + end + end + + it "sets arbitrary configuration via --config-option" do + Chef::Knife.run(%w{test yourself --config-option arbitrary_config_thing=hello}, Chef::Application::Knife.options) + expect(Chef::Config[:arbitrary_config_thing]).to eq("hello") + end + + it "handles errors in arbitrary configuration" do + expect(subcommand).to receive(:exit).with(1) + subcommand.configure_chef + expect(stderr.string).to include("ERROR: Unparsable config option \"badly_formatted_arg\"") + expect(stdout.string).to include(subcommand.opt_parser.to_s) + end + end + end describe "when first created" do diff --git a/spec/unit/mixin/powershell_out_spec.rb b/spec/unit/mixin/powershell_out_spec.rb index 8e5f3588ce..4ecdcb8325 100644 --- a/spec/unit/mixin/powershell_out_spec.rb +++ b/spec/unit/mixin/powershell_out_spec.rb @@ -18,7 +18,7 @@ require "spec_helper" require "chef/mixin/powershell_out" -describe Chef::Mixin::PowershellOut do +describe Chef::Mixin::PowershellOut, :windows_only do let(:shell_out_class) { Class.new { include Chef::Mixin::PowershellOut } } subject(:object) { shell_out_class.new } let(:architecture) { "something" } @@ -44,6 +44,18 @@ describe Chef::Mixin::PowershellOut do ).and_return(ret) expect(object.powershell_out("Get-Process", timeout: 600)).to eql(ret) end + + context "when double quote is passed in the powershell command" do + it "passes if double quote is appended with single escape" do + result = object.powershell_out("Write-Verbose \"Some String\" -Verbose") + expect(result.stderr).to be == "" + expect(result.stdout).to be == "VERBOSE: Some String\n" + end + + it "suppresses error if double quote is passed with double escape characters" do + expect { object.powershell_out("Write-Verbose \\\"Some String\\\" -Verbose") }.not_to raise_error + end + end end describe "#powershell_out!" do @@ -66,5 +78,17 @@ describe Chef::Mixin::PowershellOut do expect(mixlib_shellout).to receive(:error!) expect(object.powershell_out!("Get-Process", timeout: 600)).to eql(mixlib_shellout) end + + context "when double quote is passed in the powershell command" do + it "passes if double quote is appended with single escape" do + result = object.powershell_out!("Write-Verbose \"Some String\" -Verbose") + expect(result.stderr).to be == "" + expect(result.stdout).to be == "VERBOSE: Some String\n" + end + + it "raises error if double quote is passed with double escape characters" do + expect { object.powershell_out!("Write-Verbose \\\"Some String\\\" -Verbose") }.to raise_error(Mixlib::ShellOut::ShellCommandFailed) + end + end end end diff --git a/spec/unit/node/vivid_mash_spec.rb b/spec/unit/node/vivid_mash_spec.rb index 206b15ef6c..017e6206fc 100644 --- a/spec/unit/node/vivid_mash_spec.rb +++ b/spec/unit/node/vivid_mash_spec.rb @@ -19,36 +19,46 @@ require "spec_helper" require "chef/node/attribute_collections" describe Chef::Node::VividMash do - class Root - attr_accessor :top_level_breadcrumb - end - - let(:root) { Root.new } + let(:root) { instance_double(Chef::Node::Attribute) } let(:vivid) do - expect(root).to receive(:reset_cache).at_least(:once).with(nil) - Chef::Node::VividMash.new(root, - { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil } + Chef::Node::VividMash.new( + { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }, + root ) end - def with_breadcrumb(key) - expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original - expect(root).to receive(:top_level_breadcrumb=).with(key).at_least(:once).and_call_original + context "without a root node" do + let(:vivid) do + Chef::Node::VividMash.new( + { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil } + ) + end + + it "sets the root to the root object" do + expect(vivid["one"]["two"].__root__).to eql(vivid) + end + + it "does not send reset cache" do + # if we setup the expectation here then the object winds up responding to :reset_cache and then it fails... + # expect(vivid).not_to receive(:reset_cache) + # but even so we expect to blow up here with NoMethodError if we screw up and send :reset_cache to a root VividMash + vivid["one"]["foo"] = "bar" + end end context "#[]=" do it "deep converts values through arrays" do - allow(root).to receive(:reset_cache) - vivid[:foo] = [ { :bar => true } ] + expect(root).to receive(:reset_cache).with("foo") + vivid["foo"] = [ { :bar => true } ] expect(vivid["foo"].class).to eql(Chef::Node::AttrArray) expect(vivid["foo"][0].class).to eql(Chef::Node::VividMash) expect(vivid["foo"][0]["bar"]).to be true end it "deep converts values through nested arrays" do - allow(root).to receive(:reset_cache) - vivid[:foo] = [ [ { :bar => true } ] ] + expect(root).to receive(:reset_cache).with("foo") + vivid["foo"] = [ [ { :bar => true } ] ] expect(vivid["foo"].class).to eql(Chef::Node::AttrArray) expect(vivid["foo"][0].class).to eql(Chef::Node::AttrArray) expect(vivid["foo"][0][0].class).to eql(Chef::Node::VividMash) @@ -56,8 +66,8 @@ describe Chef::Node::VividMash do end it "deep converts values through hashes" do - allow(root).to receive(:reset_cache) - vivid[:foo] = { baz: { :bar => true } } + expect(root).to receive(:reset_cache).with("foo") + vivid["foo"] = { baz: { :bar => true } } expect(vivid["foo"]).to be_an_instance_of(Chef::Node::VividMash) expect(vivid["foo"]["baz"]).to be_an_instance_of(Chef::Node::VividMash) expect(vivid["foo"]["baz"]["bar"]).to be true @@ -66,182 +76,144 @@ describe Chef::Node::VividMash do context "#read" do before do - # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards - vivid expect(root).not_to receive(:reset_cache) end it "reads hashes deeply" do - with_breadcrumb("one") expect(vivid.read("one", "two", "three")).to eql("four") end it "does not trainwreck when hitting hash keys that do not exist" do - with_breadcrumb("one") expect(vivid.read("one", "five", "six")).to eql(nil) end it "does not trainwreck when hitting an array with an out of bounds index" do - with_breadcrumb("array") expect(vivid.read("array", 5, "one")).to eql(nil) end it "does not trainwreck when hitting an array with a string key" do - with_breadcrumb("array") expect(vivid.read("array", "one", "two")).to eql(nil) end it "does not trainwreck when traversing a nil" do - with_breadcrumb("nil") expect(vivid.read("nil", "one", "two")).to eql(nil) end end context "#exist?" do before do - # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards - vivid expect(root).not_to receive(:reset_cache) end it "true if there's a hash key there" do - with_breadcrumb("one") expect(vivid.exist?("one", "two", "three")).to be true end it "true for intermediate hashes" do - with_breadcrumb("one") expect(vivid.exist?("one")).to be true end it "true for arrays that exist" do - with_breadcrumb("array") expect(vivid.exist?("array", 1)).to be true end it "true when the value of the key is nil" do - with_breadcrumb("nil") expect(vivid.exist?("nil")).to be true end it "false when attributes don't exist" do - with_breadcrumb("one") expect(vivid.exist?("one", "five", "six")).to be false end it "false when traversing a non-container" do - with_breadcrumb("one") expect(vivid.exist?("one", "two", "three", "four")).to be false end it "false when an array index does not exist" do - with_breadcrumb("array") expect(vivid.exist?("array", 3)).to be false end it "false when traversing a nil" do - with_breadcrumb("nil") expect(vivid.exist?("nil", "foo", "bar")).to be false end end context "#read!" do before do - # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards - vivid expect(root).not_to receive(:reset_cache) end it "reads hashes deeply" do - with_breadcrumb("one") expect(vivid.read!("one", "two", "three")).to eql("four") end it "reads arrays deeply" do - with_breadcrumb("array") expect(vivid.read!("array", 1)).to eql(1) end it "throws an exception when attributes do not exist" do - with_breadcrumb("one") expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute) end it "throws an exception when traversing a non-container" do - with_breadcrumb("one") expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute) end it "throws an exception when an array element does not exist" do - with_breadcrumb("array") expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute) end end context "#write" do - before do - vivid - expect(root).not_to receive(:reset_cache).with(nil) - end - it "should write into hashes" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "five", "six") expect(vivid["one"]["five"]).to eql("six") end it "should deeply autovivify" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "five", "six", "seven", "eight", "nine", "ten") expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten") end it "should raise an exception if you overwrite an array with a hash" do - with_breadcrumb("array") expect(root).to receive(:reset_cache).at_least(:once).with("array") vivid.write("array", "five", "six") expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => "six" }, "nil" => nil }) end it "should raise an exception if you traverse through an array with a hash" do - with_breadcrumb("array") expect(root).to receive(:reset_cache).at_least(:once).with("array") vivid.write("array", "five", "six", "seven") expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => { "six" => "seven" } }, "nil" => nil }) end it "should raise an exception if you overwrite a string with a hash" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "two", "three", "four", "five") expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => "five" } } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you traverse through a string with a hash" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "two", "three", "four", "five", "six") expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => { "five" => "six" } } } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you overwrite a nil with a hash" do - with_breadcrumb("nil") expect(root).to receive(:reset_cache).at_least(:once).with("nil") vivid.write("nil", "one", "two") expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => "two" } }) end it "should raise an exception if you traverse through a nil with a hash" do - with_breadcrumb("nil") expect(root).to receive(:reset_cache).at_least(:once).with("nil") vivid.write("nil", "one", "two", "three") expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => { "two" => "three" } } }) end it "writes with a block" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write("one", "five") { "six" } expect(vivid["one"]["five"]).to eql("six") @@ -249,69 +221,55 @@ describe Chef::Node::VividMash do end context "#write!" do - before do - vivid - expect(root).not_to receive(:reset_cache).with(nil) - end - it "should write into hashes" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write!("one", "five", "six") expect(vivid["one"]["five"]).to eql("six") end it "should deeply autovivify" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write!("one", "five", "six", "seven", "eight", "nine", "ten") expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten") end it "should raise an exception if you overwrite an array with a hash" do - with_breadcrumb("array") expect(root).not_to receive(:reset_cache) expect { vivid.write!("array", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you traverse through an array with a hash" do - with_breadcrumb("array") expect(root).not_to receive(:reset_cache) expect { vivid.write!("array", "five", "six", "seven") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you overwrite a string with a hash" do - with_breadcrumb("one") expect(root).not_to receive(:reset_cache) expect { vivid.write!("one", "two", "three", "four", "five") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you traverse through a string with a hash" do - with_breadcrumb("one") expect(root).not_to receive(:reset_cache) expect { vivid.write!("one", "two", "three", "four", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you overwrite a nil with a hash" do - with_breadcrumb("nil") expect(root).not_to receive(:reset_cache) expect { vivid.write!("nil", "one", "two") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should raise an exception if you traverse through a nil with a hash" do - with_breadcrumb("nil") expect(root).not_to receive(:reset_cache) expect { vivid.write!("nil", "one", "two", "three") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "writes with a block" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") vivid.write!("one", "five") { "six" } expect(vivid["one"]["five"]).to eql("six") @@ -319,41 +277,31 @@ describe Chef::Node::VividMash do end context "#unlink" do - before do - vivid - expect(root).not_to receive(:reset_cache).with(nil) - end - it "should return nil if the keys don't already exist" do - expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original expect(root).not_to receive(:reset_cache) expect(vivid.unlink("five", "six", "seven", "eight")).to eql(nil) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should unlink hashes" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") expect( vivid.unlink("one") ).to eql({ "two" => { "three" => "four" } }) expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should unlink array elements" do - with_breadcrumb("array") expect(root).to receive(:reset_cache).at_least(:once).with("array") expect(vivid.unlink("array", 2)).to eql(2) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil }) end it "should unlink nil" do - with_breadcrumb("nil") expect(root).to receive(:reset_cache).at_least(:once).with("nil") expect(vivid.unlink("nil")).to eql(nil) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] }) end it "should traverse a nil and safely do nothing" do - with_breadcrumb("nil") expect(root).not_to receive(:reset_cache) expect(vivid.unlink("nil", "foo")).to eql(nil) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) @@ -361,41 +309,31 @@ describe Chef::Node::VividMash do end context "#unlink!" do - before do - vivid - expect(root).not_to receive(:reset_cache).with(nil) - end - it "should raise an exception if the keys don't already exist" do - expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original expect(root).not_to receive(:reset_cache) expect { vivid.unlink!("five", "six", "seven", "eight") }.to raise_error(Chef::Exceptions::NoSuchAttribute) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should unlink! hashes" do - with_breadcrumb("one") expect(root).to receive(:reset_cache).at_least(:once).with("one") expect( vivid.unlink!("one") ).to eql({ "two" => { "three" => "four" } }) expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil }) end it "should unlink! array elements" do - with_breadcrumb("array") expect(root).to receive(:reset_cache).at_least(:once).with("array") expect(vivid.unlink!("array", 2)).to eql(2) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil }) end it "should unlink! nil" do - with_breadcrumb("nil") expect(root).to receive(:reset_cache).at_least(:once).with("nil") expect(vivid.unlink!("nil")).to eql(nil) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] }) end it "should raise an exception if it traverses a nil" do - with_breadcrumb("nil") expect(root).not_to receive(:reset_cache) expect { vivid.unlink!("nil", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute) expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }) diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index 2c8fc4408b..cfc19db480 100644 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -243,6 +243,34 @@ describe Chef::Node do expect { node.sunshine = "is bright" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) end + it "does not allow modification of node attributes via hash methods" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false + node.default["h4sh"] = { foo: "bar" } + expect { node["h4sh"].delete("foo") }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) + expect { node.h4sh.delete("foo") }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) + end + + it "does not allow modification of node attributes via array methods" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false + node.default["array"] = [] + expect { node["array"] << "boom" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) + expect { node.array << "boom" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) + end + + it "returns merged immutable attributes for arrays" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false + node.default["array"] = [] + expect( node["array"].class ).to eql(Chef::Node::ImmutableArray) + expect( node.array.class ).to eql(Chef::Node::ImmutableArray) + end + + it "returns merged immutable attributes for hashes" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false + node.default["h4sh"] = {} + expect( node["h4sh"].class ).to eql(Chef::Node::ImmutableMash) + expect( node.h4sh.class ).to eql(Chef::Node::ImmutableMash) + end + it "should allow you get get an attribute via method_missing" do Chef::Config[:treat_deprecation_warnings_as_errors] = false node.default.sunshine = "is bright" @@ -756,9 +784,9 @@ describe Chef::Node do # In Chef-12.0 there is a deep_merge cache on the top level attribute which had a bug # where it cached node[:foo] separate from node['foo']. These tests exercise those edge conditions. # - # https://github.com/opscode/chef/issues/2700 - # https://github.com/opscode/chef/issues/2712 - # https://github.com/opscode/chef/issues/2745 + # https://github.com/chef/chef/issues/2700 + # https://github.com/chef/chef/issues/2712 + # https://github.com/chef/chef/issues/2745 # describe "deep merge attribute cache edge conditions" do it "does not error with complicated attribute substitution" do @@ -1681,4 +1709,107 @@ describe Chef::Node do end end + describe "path tracking via __path__" do + it "works through hash keys" do + node.default["foo"] = { "bar" => { "baz" => "qux" } } + expect(node["foo"]["bar"].__path__).to eql(%w{foo bar}) + end + + it "works through the default level" do + node.default["foo"] = { "bar" => { "baz" => "qux" } } + expect(node.default["foo"]["bar"].__path__).to eql(%w{foo bar}) + end + + it "works through arrays" do + node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ] + expect(node["foo"][0].__path__).to eql(["foo", 0]) + expect(node["foo"][0]["bar"].__path__).to eql(["foo", 0, "bar"]) + end + + it "works through arrays at the default level" do + node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ] + expect(node.default["foo"][0].__path__).to eql(["foo", 0]) + expect(node.default["foo"][0]["bar"].__path__).to eql(["foo", 0, "bar"]) + end + + # if we set __path__ in the initializer we'd get this wrong, this is why we + # update the path on every #[] or #[]= operator + it "works on access when the node has been rearranged" do + node.default["foo"] = { "bar" => { "baz" => "qux" } } + a = node.default["foo"] + node.default["fizz"] = a + expect(node["fizz"]["bar"].__path__).to eql(%w{fizz bar}) + expect(node["foo"]["bar"].__path__).to eql(%w{foo bar}) + end + + # We have a problem because the __path__ is stored on in each node, but the + # node can be wired up at multiple locations in the tree via pointers. One + # solution would be to deep-dup the value in `#[]=(key, value)` and fix the + # __path__ on all the dup'd nodes. The problem is that this would create an + # unusual situation where after assignment, you couldn't mutate the thing you + # hand a handle on. I'm not entirely positive this behavior is the correct + # thing to support, but it is more hash-like (although if we start with a hash + # then convert_value does its thing and we *do* get dup'd on assignment). This + # behavior likely makes any implementation of a deep merge cache built over the + # top of __path__ tracking have edge conditions where it will fail. + # + # Removing this support would be a breaking change. The test is included here + # because it seems most likely that someone would break this behavior while trying + # to fix __path__ behavior. + it "does not dup in the background when a node is assigned" do + # get a handle on a vividmash (can't be a hash or else we convert_value it) + node.default["foo"] = { "bar" => { "baz" => "qux" } } + a = node.default["foo"] + # assign that somewhere else in the tree + node.default["fizz"] = a + # now upate the source + a["duptest"] = true + # the tree should have been updated + expect(node.default["fizz"]["duptest"]).to be true + expect(node["fizz"]["duptest"]).to be true + end + end + + describe "root tracking via __root__" do + it "works through hash keys" do + node.default["foo"] = { "bar" => { "baz" => "qux" } } + expect(node["foo"]["bar"].__root__).to eql(node.attributes) + end + + it "works through the default level" do + node.default["foo"] = { "bar" => { "baz" => "qux" } } + expect(node.default["foo"]["bar"].__root__).to eql(node.attributes) + end + + it "works through arrays" do + node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ] + expect(node["foo"][0].__root__).to eql(node.attributes) + expect(node["foo"][0]["bar"].__root__).to eql(node.attributes) + end + + it "works through arrays at the default level" do + node.default["foo"] = [ { "bar" => { "baz" => "qux" } } ] + expect(node.default["foo"][0].__root__).to eql(node.attributes) + expect(node.default["foo"][0]["bar"].__root__).to eql(node.attributes) + end + end + + describe "ways of abusing Chef 12 node state" do + # these tests abuse the top_level_breadcrumb state in Chef 12 + it "derived attributes work correctly" do + node.default["v1"] = 1 + expect(node["a"]).to eql(nil) + node.default["a"] = node["v1"] + expect(node["a"]).to eql(1) + end + + it "works when saving nodes to variables" do + a = node.default["a"] + expect(node["a"]).to eql({}) + node.default["b"] = 0 + a["key"] = 1 + + expect(node["a"]["key"]).to eql(1) + end + end end diff --git a/spec/unit/provider/deploy_spec.rb b/spec/unit/provider/deploy_spec.rb index e69714280c..b30ddb736a 100644 --- a/spec/unit/provider/deploy_spec.rb +++ b/spec/unit/provider/deploy_spec.rb @@ -556,7 +556,7 @@ describe Chef::Provider::Deploy do @resource.deploy_to("/my/app") expect(mock_execution).to receive(:user).with("notCoolMan") expect(mock_execution).to receive(:group).with("Ggroup") - expect(mock_execution).to receive(:cwd) {|*args| + expect(mock_execution).to receive(:cwd) { |*args| if args.empty? nil else diff --git a/spec/unit/provider/group/suse_spec.rb b/spec/unit/provider/group/suse_spec.rb new file mode 100644 index 0000000000..da4e8e9155 --- /dev/null +++ b/spec/unit/provider/group/suse_spec.rb @@ -0,0 +1,90 @@ +# +# Author:: Tom Duffield (<tom@chef.io>) +# Copyright:: Copyright 2016 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Provider::Group::Suse do + let(:node) { Chef::Node.new } + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:run_context) { Chef::RunContext.new(node, {}, events) } + let(:new_members) { %w{root new_user} } + let(:new_resource) do + Chef::Resource::Group.new("new_group").tap do |r| + r.gid 50 + r.members new_members + r.system false + r.non_unique false + end + end + let(:current_resource) do + Chef::Resource::Group.new("new_group").tap do |r| + r.gid 50 + r.members %w{root} + r.system false + r.non_unique false + end + end + let(:provider) do + described_class.new(new_resource, run_context).tap do |p| + p.current_resource = current_resource + end + end + + describe "when determining the current group state" do + before(:each) do + allow(File).to receive(:exists?).and_return(true) + provider.action = :create + provider.define_resource_requirements + end + + # Checking for required binaries is already done in the spec + # for Chef::Provider::Group - no need to repeat it here. We'll + # include only what's specific to this provider. + it "should raise an error if the required binary /usr/sbin/groupmod doesn't exist" do + expect(File).to receive(:exists?).with("/usr/sbin/groupmod").and_return(false) + expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group) + end + + it "should raise error if one of the member users does not exist" do + expect(Etc).to receive(:getpwnam).with("new_user").and_raise ArgumentError + expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Group) + end + end + + describe "#set_members" do + it "should add missing members and remove deleted members" do + expect(provider).not_to receive(:remove_member) + expect(provider).to receive(:add_member).with("new_user") + provider.set_members(new_members) + end + end + + describe "#add_member" do + it "should call out to groupmod to add user" do + expect(provider).to receive(:shell_out!).with("groupmod -A new_user new_group") + provider.add_member("new_user") + end + end + + describe "#remove_member" do + it "should call out to groupmod to remove user" do + expect(provider).to receive(:shell_out!).with("groupmod -R new_user new_group") + provider.remove_member("new_user") + end + end +end diff --git a/spec/unit/provider/package/windows_spec.rb b/spec/unit/provider/package/windows_spec.rb index d1d717bdbe..53cbbc1da1 100644 --- a/spec/unit/provider/package/windows_spec.rb +++ b/spec/unit/provider/package/windows_spec.rb @@ -26,9 +26,9 @@ describe Chef::Provider::Package::Windows, :windows_only do allow(Chef::FileCache).to receive(:create_cache_path).with("package/").and_return(cache_path) end - let(:node) { double("Chef::Node") } - let(:events) { double("Chef::Events").as_null_object } # mock all the methods - let(:run_context) { double("Chef::RunContext", :node => node, :events => events) } + let(:node) { Chef::Node.new } + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:resource_source) { "calculator.msi" } let(:resource_name) { "calculator" } let(:installer_type) { nil } diff --git a/spec/unit/provider/remote_file/content_spec.rb b/spec/unit/provider/remote_file/content_spec.rb index db9a75458d..307eb98187 100644 --- a/spec/unit/provider/remote_file/content_spec.rb +++ b/spec/unit/provider/remote_file/content_spec.rb @@ -159,7 +159,7 @@ describe Chef::Provider::RemoteFile::Content do describe "when there is an array of sources and the first fails" do - # https://github.com/opscode/chef/pull/1358#issuecomment-40853299 + # https://github.com/chef/chef/pull/1358#issuecomment-40853299 def create_exception(exception_class) if [ Net::HTTPServerException, Net::HTTPFatalError ].include? exception_class exception_class.new("message", { "something" => 1 }) diff --git a/spec/unit/provider/user_spec.rb b/spec/unit/provider/user_spec.rb index 1a8ad6ad1b..719dc8d492 100644 --- a/spec/unit/provider/user_spec.rb +++ b/spec/unit/provider/user_spec.rb @@ -221,6 +221,12 @@ describe Chef::Provider::User do it "should return false if the objects are identical" do expect(@provider.compare_user).to eql(false) end + + it "should ignore differences in trailing slash in home paths" do + @new_resource.home "/home/adam" + @current_resource.home "/home/adam/" + expect(@provider.compare_user).to eql(false) + end end describe "action_create" do diff --git a/spec/unit/resource/apt_repository_spec.rb b/spec/unit/resource/apt_repository_spec.rb index 0b0c0c5d26..69cf94ae56 100644 --- a/spec/unit/resource/apt_repository_spec.rb +++ b/spec/unit/resource/apt_repository_spec.rb @@ -24,7 +24,7 @@ describe Chef::Resource::AptRepository do let(:run_context) { Chef::RunContext.new(node, {}, events) } let(:resource) { Chef::Resource::AptRepository.new("multiverse", run_context) } - it "should create a new Chef::Resource::AptUpdate" do + it "should create a new Chef::Resource::AptRepository" do expect(resource).to be_a_kind_of(Chef::Resource) expect(resource).to be_a_kind_of(Chef::Resource::AptRepository) end diff --git a/version_policy.rb b/version_policy.rb index bfe08e91e4..5621ea43ff 100644 --- a/version_policy.rb +++ b/version_policy.rb @@ -45,7 +45,7 @@ OMNIBUS_OVERRIDES = { ## These can float as they are frequently updated in a way that works for us #override "cacerts" =>"???", - "openssl" => "1.0.2h", + "openssl" => "1.0.2j", } # |