diff options
194 files changed, 4956 insertions, 1150 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 549b63abba..c9b30edba7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Change Log +## [v12.12.15](https://github.com/chef/chef/tree/v12.12.15) (2016-07-08) +[Full Changelog](https://github.com/chef/chef/compare/v12.12.13...v12.12.15) + +**Fixed Bugs:** + +- Fix for #5094 12.12.13 node.default_unless issue [\#5097](https://github.com/chef/chef/pull/5097) ([lamont-granquist](https://github.com/lamont-granquist)) + +## [v12.12.13](https://github.com/chef/chef/tree/v12.12.13) (2016-07-01) +[Full Changelog](https://github.com/chef/chef/compare/v12.11.18...v12.12.13) + +**Implemented Enhancements:** + +- Tweak 3694 warnings [\#5075](https://github.com/chef/chef/pull/5075) ([lamont-granquist](https://github.com/lamont-granquist)) +- Adding node object to Data collector run\_converge message [\#5065](https://github.com/chef/chef/pull/5065) ([adamleff](https://github.com/adamleff)) +- Attribute API improvements [\#5029](https://github.com/chef/chef/pull/5029) ([lamont-granquist](https://github.com/lamont-granquist)) +- Remove deprecated Thread.exclusive around require call. [\#5068](https://github.com/chef/chef/pull/5068) ([maxlazio](https://github.com/maxlazio)) +- Ensure that chef-solo uses the expected repo dir [\#5059](https://github.com/chef/chef/pull/5059) ([thommay](https://github.com/thommay)) +- Expand data\_collector resource list to include all resources [\#5058](https://github.com/chef/chef/pull/5058) ([adamleff](https://github.com/adamleff)) +- Turn off fips with an empty environment var [\#5048](https://github.com/chef/chef/pull/5048) ([mwrock](https://github.com/mwrock)) +- Deprecate knife-supermarket gem [\#4896](https://github.com/chef/chef/pull/4896) ([thommay](https://github.com/thommay)) +- Update Nokogiri [\#5042](https://github.com/chef/chef/pull/5042) ([mwrock](https://github.com/mwrock)) +- Remote resource should respect sensitive flag [\#5025](https://github.com/chef/chef/pull/5025) ([PrajaktaPurohit](https://github.com/PrajaktaPurohit)) +- Convert the 3694 warning to a deprecation so it will be subject to the usual deprecation formatting \(collected at the bottom, can be made an error, etc\). [\#5022](https://github.com/chef/chef/pull/5022) ([coderanger](https://github.com/coderanger)) +- Deprecate `knife cookbook create` in favor of `chef generate cookbook`. [\#5021](https://github.com/chef/chef/pull/5021) ([tylercloke](https://github.com/tylercloke)) + +**Fixed Bugs:** + +- Fixes windows_package uninstall scenarios by calling uninstall string directly [\#5050](https://github.com/chef/chef/pull/5050) ([mwrock](https://github.com/mwrock)) +- Fix gem_package idempotency [\#5046](https://github.com/chef/chef/pull/5046) ([thommay](https://github.com/thommay)) +- Undefined local variable lookup in multiplexed_dir.rb [\#5027](https://github.com/chef/chef/issues/5027) ([robdimarco](https://github.com/robdimarco)) +- Correctly write out data collector metadata file [\#5019](https://github.com/chef/chef/pull/5019) ([adamleff](https://github.com/adamleff)) +- Eliminate missing constant errors for LWRP class [\#5000](https://github.com/chef/chef/pull/5000) ([PrajaktaPurohit](https://github.com/PrajaktaPurohit)) +- Updated_resource_count to data collector should only include updated resources [\#5006](https://github.com/chef/chef/pull/5006) ([adamleff](https://github.com/adamleff)) +- Don't mask directory deletion errors [\#4991](https://github.com/chef/chef/pull/4991) ([jaymzh](https://github.com/jaymzh)) + ## [v12.11.18](https://github.com/chef/chef/tree/v12.11.18) (2016-06-02) [Full Changelog](https://github.com/chef/chef/compare/v12.11.17...v12.11.18) @@ -22,6 +22,7 @@ group(:omnibus_package) do gem "rb-readline" gem "nokogiri" end + group(:omnibus_package, :pry) do gem "pry" gem "pry-byebug" @@ -65,7 +66,7 @@ end group(:development, :test) do gem "simplecov" - gem "rack" + gem "rack", "< 2.0" # 2.0 requires Ruby 2.2+ # for testing new chefstyle rules # gem 'chefstyle', github: 'chef/chefstyle' @@ -78,7 +79,7 @@ end group(:travis) do # See `bundler-audit` in .travis.yml - gem "bundler-audit", git: "https://github.com/rubysec/bundler-audit.git", ref: "4e32fca" + gem "bundler-audit", git: "https://github.com/rubysec/bundler-audit.git" end instance_eval(ENV["GEMFILE_MOD"]) if ENV["GEMFILE_MOD"] diff --git a/Gemfile.lock b/Gemfile.lock index 5cd0539227..33bdcfb214 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,27 +8,27 @@ GIT GIT remote: https://github.com/rubysec/bundler-audit.git - revision: 4e32fca89d75f0e249671431ff38aadc02bfb28b - ref: 4e32fca + revision: b16b26e0a4eed474f5f1ee38c22030a2f617ffc8 specs: - bundler-audit (0.4.0) + bundler-audit (0.5.0) bundler (~> 1.2) thor (~> 0.18) PATH remote: . specs: - chef (12.12.2) + chef (12.13.21) bundler (>= 1.10) - chef-config (= 12.12.2) - chef-zero (~> 4.5) + chef-config (= 12.13.21) + chef-zero (~> 4.8) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) ffi-yajl (~> 2.2) highline (~> 1.6, >= 1.6.9) iniparse (~> 1.4) + mixlib-archive (>= 0.2.0) mixlib-authentication (~> 1.4) - mixlib-cli (~> 1.4) + mixlib-cli (~> 1.4, < 1.7) mixlib-log (~> 1.3) mixlib-shellout (~> 2.0) net-sftp (~> 2.1, >= 2.1.2) @@ -37,26 +37,27 @@ PATH ohai (>= 8.6.0.alpha.1, < 9) plist (~> 3.2) proxifier (~> 1.0) - rspec-core (~> 3.4) - rspec-expectations (~> 3.4) - rspec-mocks (~> 3.4) + rspec-core (~> 3.5) + rspec-expectations (~> 3.5) + rspec-mocks (~> 3.5) rspec_junit_formatter (~> 0.2.0) serverspec (~> 2.7) specinfra (~> 2.10) syslog-logger (~> 1.6) uuidtools (~> 2.1.5) - chef (12.12.2-universal-mingw32) + chef (12.13.21-universal-mingw32) bundler (>= 1.10) - chef-config (= 12.12.2) - chef-zero (~> 4.5) + chef-config (= 12.13.21) + chef-zero (~> 4.8) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) ffi (~> 1.9) ffi-yajl (~> 2.2) highline (~> 1.6, >= 1.6.9) iniparse (~> 1.4) + mixlib-archive (>= 0.2.0) mixlib-authentication (~> 1.4) - mixlib-cli (~> 1.4) + mixlib-cli (~> 1.4, < 1.7) mixlib-log (~> 1.3) mixlib-shellout (~> 2.0) net-sftp (~> 2.1, >= 2.1.2) @@ -65,9 +66,9 @@ PATH ohai (>= 8.6.0.alpha.1, < 9) plist (~> 3.2) proxifier (~> 1.0) - rspec-core (~> 3.4) - rspec-expectations (~> 3.4) - rspec-mocks (~> 3.4) + rspec-core (~> 3.5) + rspec-expectations (~> 3.5) + rspec-mocks (~> 3.5) rspec_junit_formatter (~> 0.2.0) serverspec (~> 2.7) specinfra (~> 2.10) @@ -87,7 +88,7 @@ PATH PATH remote: chef-config specs: - chef-config (12.12.2) + chef-config (12.13.21) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) @@ -98,14 +99,14 @@ GEM addressable (2.4.0) appbundler (0.9.0) mixlib-cli (~> 1.4) - artifactory (2.3.2) + artifactory (2.3.3) ast (2.3.0) - aws-sdk (2.3.13) - aws-sdk-resources (= 2.3.13) - aws-sdk-core (2.3.13) + aws-sdk (2.3.19) + aws-sdk-resources (= 2.3.19) + aws-sdk-core (2.3.19) jmespath (~> 1.0) - aws-sdk-resources (2.3.13) - aws-sdk-core (= 2.3.13) + aws-sdk-resources (2.3.19) + aws-sdk-core (= 2.3.19) aws-sdk-v1 (1.66.0) json (~> 1.4) nokogiri (>= 1.4.4) @@ -116,7 +117,7 @@ GEM chef-api (0.6.0) logify (~> 0.1) mime-types - chef-provisioning (1.7.1) + chef-provisioning (1.8.0) cheffish (>= 1.3.1, < 3.0) inifile (>= 2.0.2) mixlib-install (~> 1.0) @@ -124,7 +125,7 @@ GEM net-ssh (>= 2.9, < 4.0) net-ssh-gateway (~> 1.2.0) winrm (~> 1.3) - chef-provisioning-aws (1.9.0) + chef-provisioning-aws (1.10.0) aws-sdk (>= 2.1.26, < 3.0) aws-sdk-v1 (>= 1.59.0) chef-provisioning (~> 1.4) @@ -132,11 +133,11 @@ GEM ubuntu_ami (~> 0.4, >= 0.4.1) chef-rewind (0.0.9) chef-sugar (3.3.0) - chef-zero (4.6.2) + chef-zero (4.8.0) ffi-yajl (~> 2.2) hashie (>= 2.0, < 4.0) mixlib-log (~> 1.3) - rack + rack (< 2) uuidtools (~> 2.1) cheffish (2.0.4) chef-zero (~> 4.3) @@ -146,7 +147,7 @@ GEM fauxhai (~> 3.2) rspec (~> 3.0) coderay (1.1.1) - colorize (0.7.7) + colorize (0.8.1) compat_resource (12.10.6) cucumber-core (1.5.0) gherkin (~> 4.0) @@ -158,10 +159,12 @@ GEM erubis (2.7.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) - fauxhai (3.5.0) + fauxhai (3.6.0) net-ssh ffi (1.9.10) ffi (1.9.10-x86-mingw32) + ffi-win32-extensions (1.0.2) + ffi ffi-yajl (2.2.3) libyajl2 (~> 1.2) foodcritic (6.3.0) @@ -174,7 +177,7 @@ GEM yajl-ruby (~> 1.1) fuzzyurl (0.8.0) gherkin (4.0.0) - github_api (0.14.0) + github_api (0.14.2) addressable (~> 2.4.0) descendants_tracker (~> 0.0.4) faraday (~> 0.8, < 0.10) @@ -188,10 +191,10 @@ GEM ffi (>= 1.0.1) gyoku (1.3.1) builder (>= 2.1.2) - halite (1.2.1) + halite (1.3.0) bundler chef (~> 12.0) - stove (~> 3.2, >= 3.2.3) + stove (~> 4.0) thor hashie (3.4.4) highline (1.7.8) @@ -217,11 +220,13 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.1.0) + mixlib-archive (0.2.0) + mixlib-log mixlib-authentication (1.4.1) mixlib-log mixlib-cli (1.6.0) mixlib-config (2.2.1) - mixlib-install (1.0.13) + mixlib-install (1.1.0) artifactory mixlib-shellout mixlib-versioning @@ -238,7 +243,7 @@ GEM net-ssh (>= 2.6.5) net-sftp (2.1.2) net-ssh (>= 2.6.5) - net-ssh (3.1.1) + net-ssh (3.2.0) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) net-ssh-multi (1.2.1) @@ -261,7 +266,7 @@ GEM rack (>= 1.2, < 3) octokit (4.3.0) sawyer (~> 0.7.0, >= 0.5.3) - ohai (8.16.0) + ohai (8.17.1) chef-config (>= 12.5.0.alpha.1, < 13) ffi (~> 1.9) ffi-yajl (~> 2.2) @@ -277,7 +282,7 @@ GEM ast (~> 2.2) pkg-config (1.1.7) plist (3.2.0) - poise (2.7.0) + poise (2.7.1) halite (~> 1.0) polyglot (0.3.5) powerpack (0.1.1) @@ -297,25 +302,25 @@ GEM pry (>= 0.9.11) rack (1.6.4) rainbow (2.1.0) - rake (11.1.2) + rake (11.2.2) rb-readline (0.5.3) retryable (2.0.3) - rspec (3.4.0) - rspec-core (~> 3.4.0) - rspec-expectations (~> 3.4.0) - rspec-mocks (~> 3.4.0) - rspec-core (3.4.4) - rspec-support (~> 3.4.0) - rspec-expectations (3.4.0) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.1) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) + rspec-support (~> 3.5.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.4.1) + rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) - rspec-support (3.4.1) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) rspec_junit_formatter (0.2.3) builder (< 4) rspec-core (>= 2, < 4, != 2.12.0) @@ -345,23 +350,23 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.0) slop (3.6.0) - specinfra (2.59.0) + specinfra (2.59.4) net-scp net-ssh (>= 2.7, < 4.0) net-telnet sfl - stove (3.2.8) + stove (4.1.1) chef-api (~> 0.5) logify (~> 0.2) syslog-logger (1.6.8) systemu (2.6.5) thor (0.19.1) thread_safe (0.3.5) - tomlrb (1.2.1) + tomlrb (1.2.2) treetop (1.6.5) polyglot (~> 0.3) ubuntu_ami (0.4.1) - unicode-display_width (1.0.5) + unicode-display_width (1.1.0) uuidtools (2.1.5) win32-api (1.5.3-universal-mingw32) win32-dir (0.5.1) @@ -370,7 +375,7 @@ GEM win32-ipc (>= 0.6.0) win32-eventlog (0.6.3) ffi - win32-ipc (0.6.6) + win32-ipc (0.7.0) ffi win32-mmap (0.4.2) ffi @@ -378,8 +383,9 @@ GEM win32-ipc (>= 0.6.0) win32-process (0.8.3) ffi (>= 1.0.0) - win32-service (0.8.7) + win32-service (0.8.9) ffi + ffi-win32-extensions windows-api (0.4.4) win32-api (>= 1.4.5) winrm (1.8.1) @@ -423,7 +429,7 @@ DEPENDENCIES pry-byebug pry-remote pry-stack_explorer - rack + rack (< 2.0) rake rb-readline ruby-prof diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 4d99cec89f..b3bba87dc0 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,57 +1,35 @@ *This file holds "in progress" release notes for the current release under development and is intended for consumption by the Chef Documentation team. Please see `https://docs.chef.io/release/<major>-<minor>/release_notes.html` for the official Chef release notes.* -# Chef Client Release Notes 12.11: +# Chef Client Release Notes 12.12: -## Support for RFC 062-based exit codes +## Attribute read/write/unlink/exist? APIs -[Chef RFC 062](https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md) identifies standard exit codes for Chef Client. As of this release, When Chef exits with a non-standard exit code, a deprecation warning will be printed. +On the node object: -Also introduced is a new configuration setting - `exit_status`. +- `node.read("foo", "bar", "baz")` equals `node["foo"]["bar"]["baz"] `but with safety (nil instead of exception) +- `node.read!("foo", "bar", "baz")` equals `node["foo"]["bar"]["baz"]` and does raises NoMethodError -By default in this release, `exit_status` is `nil` and the default behavior will be to warn on the use of deprecated and non-standard exit codes. `exit_status` can be set to `:enabled`, which will force chef-client to exit with the RFC defined exit codes and any non-standard exit statuses will be converted to `1` or GENERIC_FAILURE. `exit_status` can also be set to `:disabled` which preserves the old behavior of non-standardized exit status and skips the deprecation warnings. +- `node.write(:default, "foo", "bar", "baz")` equals `node.default["foo"]["bar"] = "baz"` and autovivifies and replaces intermediate non-hash objects (very safe) +- `node.write!(:default, "foo", "bar", "baz")` equals `node.default["foo"]["bar"] = "baz"` and while it autovivifies it can raise if you hit a non-hash on an intermediate key (NoMethodError) +- there is still no non-autovivifying writer, and i don't think anyone really wants one. +- `node.exist?("foo", "bar")` can be used to see if `node["foo"]["bar"]` exists -## New Data Collector functionality for run statistics +On node levels: -The Data Collector feature is new to Chef 12.11 and is detailed in [Chef RFC 077](https://github.com/chef/chef-rfc/blob/master/rfc077-mode-agnostic-data-collection.md). It provides a unified method for sharing statistics about your Chef runs in a webhook-like manner. The Data Collector supports Chef in all its modes: Chef Client, Chef Solo (commonly referred to as "Chef Client Local Mode"), and Chef Solo legacy mode. +- `node.default.read/read!("foo")` operates similarly to `node.read("foo")` but only on default level +- `node.default.write/write!("foo", "bar")` is `node.write/write!(:default, "foo", "bar")` +- `node.default.unlink/unlink!("foo")` is `node.unlink/unlink!(:default, "foo")` +- `node.default.exist?("foo", "bar")` can be used to see if `node.default["foo"]["bar"]` exists -To enable the Data Collector, specify the following settings in your client configuration file: +Deprecations: - * `data_collector.server_url`: Required. The URL to which the Chef Client will POST the Data Collector messages - * `data_collector.token`: Optional. An token which will be sent in a `x-data-collector-token` HTTP header which can be used to authenticate the message. - * `data_collector.mode`: The Chef mode in which the Data Collector should run. For example, this allows you to only enable Data Collector in Chef Solo but not Chef Client. Available options are `:solo`, `:client`, or `:both`. Default is `:both`. - * `data_collector.raise_on_failure`: If enabled, Chef will raise an exception and fail to run if the Data Collector cannot be reached at the start of the Chef run. Defaults to `false`. - * `data_collector.organization`: Optional. In Solo mode, the `organization` field in the messages will be set to this value. Default is `chef_solo`. This field does not apply to Chef Client mode. +- node.set is deprecated +- node.set_unless is deprecated -## Replace chef-solo with chef-client local mode +## `data_collector` enhancements -The default operation of `chef-solo` is now the equivalent to `chef-client -z`, but allows for the old style `chef-solo` by uttering `chef-solo --legacy-mode`. As part of this effort, environment and role files written in ruby are now fully supported by `knife upload`. +- Adds `node` to the `data_collector` message +- `data_collector` reports on all resources and not just those that have been processed -## Added a `systemd_unit` resource - -A new `systemd_unit` resource is now available. This resource supports the following properties: - -* `enabled` - boolean -* `active` - boolean -* `masked` - boolean -* `static` - boolean -* `user` - String -* `content` - String or Hash -* `triggers_reload` - boolean - -It has these possible actions: - -* `:nothing` - default -* `:create` -* `:delete` -* `:enable` -* `:disable` -* `:mask` -* `:unmask` -* `:start` -* `:stop` -* `:restart` -* `:reload` -* `:try_restart` -* `:reload_or_restart` -* `:reload_or_try_restart` +## `knife cookbook create` is deprecated. Use the chef-dk's `chef generate cookbook` instead.
\ No newline at end of file @@ -29,12 +29,13 @@ require_relative "tasks/cbgb" require_relative "tasks/dependencies" require_relative "tasks/changelog" -ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "Chef") do |package| +ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "Chef", "chef") do |package| package.component_paths = ["chef-config"] package.generate_version_class = true end # Add a conservative dependency update to version:bump (which was created by PackageTask) -task "version:bump" => %w{version:bump_patch version:update bundle:install} +task "version:bump" => %w{version:bump_patch version:update} +task "version:bump" => %w{version:bump_patch version:update} task :pedant, :chef_zero_spec @@ -1 +1 @@ -12.12.2
\ No newline at end of file +12.13.21
\ No newline at end of file diff --git a/acceptance/.gitignore b/acceptance/.gitignore index c2ab70737d..4b0b151d75 100644 --- a/acceptance/.gitignore +++ b/acceptance/.gitignore @@ -1 +1,3 @@ .acceptance_logs +.acceptance_data +data-collector/Berksfile.lock diff --git a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml b/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml index 40228829bb..dfe3d888a0 100644 --- a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml +++ b/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml @@ -213,18 +213,7 @@ platforms: image-type: machine user_data: | <powershell> - $logfile="C:\\Program Files\\Amazon\\Ec2ConfigService\\Logs\\kitchen-ec2.log" - #PS Remoting and & winrm.cmd basic config - Enable-PSRemoting -Force -SkipNetworkProfileCheck - & winrm.cmd set winrm/config '@{MaxTimeoutms="1800000"}' >> $logfile - & winrm.cmd set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}' >> $logfile - & winrm.cmd set winrm/config/winrs '@{MaxShellsPerUser="50"}' >> $logfile - #Server settings - support username/password login - & winrm.cmd set winrm/config/service/auth '@{Basic="true"}' >> $logfile - & winrm.cmd set winrm/config/service '@{AllowUnencrypted="true"}' >> $logfile - & winrm.cmd set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}' >> $logfile - #Firewall Config - & netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public protocol=tcp localport=5985 remoteip=localsubnet new remoteip=any >> $logfile + & netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public protocol=tcp localport=5985 remoteip=localsubnet new remoteip=any #Set script execution to unrestricted & Set-ExecutionPolicy Unrestricted -Force </powershell> diff --git a/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb b/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb index d36909e8b3..d5d2e1380b 100644 --- a/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb +++ b/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb @@ -1,3 +1,5 @@ +require 'chef/mixin/shell_out' + module KitchenAcceptance class Kitchen < Chef::Resource resource_name :kitchen @@ -33,18 +35,33 @@ module KitchenAcceptance property :kitchen_options, String, default: lazy { ENV["PROJECT_NAME"] ? "-c -l debug" : "-c" } action :run do - execute "bundle exec kitchen #{command}#{instances ? " #{instances}" : ""}#{kitchen_options ? " #{kitchen_options}" : ""}" do - cwd kitchen_dir - env({ - "KITCHEN_DRIVER" => driver, - "KITCHEN_INSTANCES" => instances, - "KITCHEN_LOCAL_YAML" => ::File.expand_path("../../.kitchen.#{driver}.yml", __FILE__), - "KITCHEN_CHEF_PRODUCT" => chef_product, - "KITCHEN_CHEF_CHANNEL" => chef_channel, - "KITCHEN_CHEF_VERSION" => chef_version, - "ARTIFACTORY_USERNAME" => artifactory_username, - "ARTIFACTORY_PASSWORD" => artifactory_password - }.merge(new_resource.env)) + + ruby_block "copy_kitchen_logs_to_data_path" do + block do + cmd_env = { + "KITCHEN_DRIVER" => driver, + "KITCHEN_INSTANCES" => instances, + "KITCHEN_LOCAL_YAML" => ::File.expand_path("../../.kitchen.#{driver}.yml", __FILE__), + "KITCHEN_CHEF_PRODUCT" => chef_product, + "KITCHEN_CHEF_CHANNEL" => chef_channel, + "KITCHEN_CHEF_VERSION" => chef_version, + "ARTIFACTORY_USERNAME" => artifactory_username, + "ARTIFACTORY_PASSWORD" => artifactory_password + }.merge(new_resource.env) + suite = kitchen_dir.split("/").last + kitchen_log_path = ENV["WORKSPACE"] ? "#{ENV["WORKSPACE"]}/chef-acceptance-data/logs" : "#{kitchen_dir}/../.acceptance_data/logs/" + + begin + shell_out!("bundle exec kitchen #{command}#{instances ? " #{instances}" : ""}#{kitchen_options ? " #{kitchen_options}" : ""}", + env: cmd_env, + timeout: 60 * 30, + live_stream: STDOUT, + cwd: kitchen_dir) + ensure + FileUtils.mkdir_p("#{kitchen_log_path}/#{suite}/#{command}") + FileUtils.cp_r("#{kitchen_dir}/.kitchen/logs/.", "#{kitchen_log_path}/#{suite}/#{command}") + end + end end end end diff --git a/acceptance/Gemfile b/acceptance/Gemfile index 185437ed71..b521df8607 100644 --- a/acceptance/Gemfile +++ b/acceptance/Gemfile @@ -1,13 +1,16 @@ source "https://rubygems.org" gem "chef-acceptance", github: "chef/chef-acceptance" -gem "test-kitchen" gem "kitchen-ec2" -gem "kitchen-inspec" gem "inspec" # Pinning to github for kitchen-vagrant because 0.19.0 incorrectly # puts in a box_url for bento when a vagrant box in atlas is specified gem "kitchen-vagrant" gem "windows_chef_zero" -gem "winrm-fs" +gem "kitchen-inspec", :github => "mwrock/kitchen-inspec", :branch => "winrm-v2" +gem "test-kitchen", :github => "test-kitchen", :branch => "winrm-v2" +gem "train", :github => "chef/train", :branch => "winrm-v2" +gem "winrm", :github => "winrb/winrm", :branch => "winrm-v2" +gem "winrm-fs", :github => "winrb/winrm-fs", :branch => "winrm-v2" +gem "winrm-elevated", :github => "winrb/winrm-elevated", :branch => "winrm-v2" gem "berkshelf" diff --git a/acceptance/Gemfile.lock b/acceptance/Gemfile.lock index 840e1a49c4..ff9d439b5b 100644 --- a/acceptance/Gemfile.lock +++ b/acceptance/Gemfile.lock @@ -6,18 +6,89 @@ GIT mixlib-shellout (~> 2.0) thor (~> 0.19) +GIT + remote: git://github.com/chef/train.git + revision: cc92f89267f128ee78bc12da803722268e2f6af2 + branch: winrm-v2 + specs: + train (0.15.0) + docker-api (~> 1.26) + json (~> 1.8) + mixlib-shellout (~> 2.0) + net-scp (~> 1.2) + net-ssh (>= 2.9, < 4.0) + winrm (~> 2.0) + winrm-fs (~> 1.0) + +GIT + remote: git://github.com/mwrock/kitchen-inspec.git + revision: d63240a44667974070298ad4fca20394257c92bd + branch: winrm-v2 + specs: + kitchen-inspec (0.14.0) + inspec (>= 0.22.0, < 1.0.0) + test-kitchen (~> 1.6) + +GIT + remote: git://github.com/test-kitchen/test-kitchen.git + revision: 06d7c0168ed6390c87a821ae8958e7d80c4d1912 + branch: winrm-v2 + specs: + test-kitchen (1.10.2) + mixlib-install (~> 1.0, >= 1.0.4) + mixlib-shellout (>= 1.2, < 3.0) + net-scp (~> 1.1) + net-ssh (>= 2.9, < 4.0) + safe_yaml (~> 1.0) + thor (~> 0.18) + +GIT + remote: git://github.com/winrb/winrm-elevated.git + revision: 160bf424e018b2539fdde73079443e565ae57260 + branch: winrm-v2 + specs: + winrm-elevated (1.0.0) + winrm (~> 2.0) + winrm-fs (~> 1.0) + +GIT + remote: git://github.com/winrb/winrm-fs.git + revision: ceb883b87fb81898e73e9249f287457ccb9d549f + branch: winrm-v2 + specs: + winrm-fs (1.0.0) + erubis (~> 2.7) + logging (>= 1.6.1, < 3.0) + rubyzip (~> 1.1) + winrm + +GIT + remote: git://github.com/winrb/winrm.git + revision: 0f35bbd963024b32cc8e262f25e6110c6abf5794 + branch: winrm-v2 + specs: + winrm (2.0.0) + builder (>= 2.1.2) + erubis (~> 2.7) + gssapi (~> 1.2) + gyoku (~> 1.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (>= 1.6.1, < 3.0) + nori (~> 2.0) + rubyntlm (~> 0.6.0) + GEM remote: https://rubygems.org/ specs: addressable (2.4.0) - artifactory (2.3.2) - aws-sdk (2.3.9) - aws-sdk-resources (= 2.3.9) - aws-sdk-core (2.3.9) + artifactory (2.3.3) + aws-sdk (2.4.2) + aws-sdk-resources (= 2.4.2) + aws-sdk-core (2.4.2) jmespath (~> 1.0) - aws-sdk-resources (2.3.9) - aws-sdk-core (= 2.3.9) - berkshelf (4.3.3) + aws-sdk-resources (2.4.2) + aws-sdk-core (= 2.4.2) + berkshelf (4.3.5) addressable (~> 2.3, >= 2.3.4) berkshelf-api-client (~> 2.0, >= 2.0.2) buff-config (~> 1.0) @@ -29,6 +100,7 @@ GEM faraday (~> 0.9) httpclient (~> 2.7) minitar (~> 0.5, >= 0.5.4) + mixlib-archive (~> 0.1) octokit (~> 4.0) retryable (~> 2.0) ridley (~> 4.5) @@ -52,21 +124,21 @@ GEM celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.10.24) + chef-config (12.12.15) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) cleanroom (1.0.0) coderay (1.1.1) diff-lcs (1.2.5) - docker-api (1.28.0) + docker-api (1.29.1) excon (>= 0.38.0) json erubis (2.7.0) - excon (0.49.0) + excon (0.51.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) - ffi (1.9.10) + ffi (1.9.14) fuzzyurl (0.8.0) gssapi (1.2.0) ffi (>= 1.0.1) @@ -75,30 +147,25 @@ GEM hashie (3.4.4) hitimes (1.2.4) httpclient (2.7.2) - inspec (0.22.1) + inspec (0.26.0) hashie (~> 3.4) json (~> 1.8) method_source (~> 0.8) pry (~> 0) - r-train (~> 0.12) rainbow (~> 2) rspec (~> 3) rspec-its (~> 1.2) rubyzip (~> 1.1) thor (~> 0.19) - jmespath (1.2.4) - json_pure (>= 1.8.1) + train (~> 0.13) + jmespath (1.3.1) json (1.8.3) - json_pure (1.8.3) - kitchen-ec2 (1.0.0) + kitchen-ec2 (1.0.1) aws-sdk (~> 2) excon multi_json retryable (~> 2.0) test-kitchen (~> 1.4, >= 1.4.1) - kitchen-inspec (0.14.0) - inspec (>= 0.22.0, < 1.0.0) - test-kitchen (~> 1.6) kitchen-vagrant (0.20.0) test-kitchen (~> 1.4) little-plugger (1.1.4) @@ -107,13 +174,12 @@ GEM multi_json (~> 1.10) method_source (0.8.2) minitar (0.5.4) - mixlib-authentication (1.4.0) + mixlib-archive (0.2.0) + mixlib-log + mixlib-authentication (1.4.1) mixlib-log - rspec-core (~> 3.2) - rspec-expectations (~> 3.2) - rspec-mocks (~> 3.2) mixlib-config (2.2.1) - mixlib-install (1.0.12) + mixlib-install (1.1.0) artifactory mixlib-shellout mixlib-versioning @@ -125,26 +191,18 @@ GEM multipart-post (2.0.0) net-scp (1.2.1) net-ssh (>= 2.6.5) - net-ssh (3.1.1) + net-ssh (3.2.0) nio4r (1.2.1) nori (2.6.0) octokit (4.3.0) sawyer (~> 0.7.0, >= 0.5.3) - pry (0.10.3) + pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - r-train (0.12.1) - docker-api (~> 1.26) - json (~> 1.8) - mixlib-shellout (~> 2.0) - net-scp (~> 1.2) - net-ssh (>= 2.9, < 4.0) - winrm (~> 1.6) - winrm-fs (~> 0.3) rainbow (2.1.0) - retryable (2.0.3) - ridley (4.5.1) + retryable (2.0.4) + ridley (4.6.0) addressable buff-config (~> 1.0) buff-extensions (~> 1.0) @@ -162,22 +220,22 @@ GEM retryable (~> 2.0) semverse (~> 1.1) varia_model (~> 0.4.0) - rspec (3.4.0) - rspec-core (~> 3.4.0) - rspec-expectations (~> 3.4.0) - rspec-mocks (~> 3.4.0) - rspec-core (3.4.4) - rspec-support (~> 3.4.0) - rspec-expectations (3.4.0) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.1) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) + rspec-support (~> 3.5.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.4.1) + rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) - rspec-support (3.4.1) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) rubyntlm (0.6.0) rubyzip (1.2.0) safe_yaml (1.0.4) @@ -189,13 +247,6 @@ GEM solve (2.0.3) molinillo (~> 0.4.2) semverse (~> 1.1) - test-kitchen (1.9.0) - mixlib-install (~> 1.0, >= 1.0.4) - mixlib-shellout (>= 1.2, < 3.0) - net-scp (~> 1.1) - net-ssh (>= 2.9, < 4.0) - safe_yaml (~> 1.0) - thor (~> 0.18) thor (0.19.1) timers (4.0.4) hitimes @@ -204,19 +255,6 @@ GEM hashie (>= 2.0.2, < 4.0.0) windows_chef_zero (2.0.0) test-kitchen (>= 1.2.1) - winrm (1.8.1) - builder (>= 2.1.2) - gssapi (~> 1.2) - gyoku (~> 1.0) - httpclient (~> 2.2, >= 2.2.0.2) - logging (>= 1.6.1, < 3.0) - nori (~> 2.0) - rubyntlm (~> 0.6.0) - winrm-fs (0.4.2) - erubis (~> 2.7) - logging (>= 1.6.1, < 3.0) - rubyzip (~> 1.1) - winrm (~> 1.5) PLATFORMS ruby @@ -226,11 +264,14 @@ DEPENDENCIES chef-acceptance! inspec kitchen-ec2 - kitchen-inspec + kitchen-inspec! kitchen-vagrant - test-kitchen + test-kitchen! + train! windows_chef_zero - winrm-fs + winrm! + winrm-elevated! + winrm-fs! BUNDLED WITH 1.12.5 diff --git a/acceptance/data-collector/Berksfile.lock b/acceptance/data-collector/Berksfile.lock deleted file mode 100644 index 39f4ce30dc..0000000000 --- a/acceptance/data-collector/Berksfile.lock +++ /dev/null @@ -1,6 +0,0 @@ -DEPENDENCIES - data-collector-test - path: .acceptance/data-collector-test - -GRAPH - data-collector-test (0.1.0) diff --git a/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb b/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb index be15b96429..f9d365ac58 100644 --- a/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb +++ b/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb @@ -110,6 +110,7 @@ shared_examples_for "run_converge.success payload check" do expanded_run_list message_type message_version + node node_name organization_name resources @@ -150,6 +151,7 @@ shared_examples_for "run_converge.failure payload check" do expanded_run_list message_type message_version + node node_name organization_name resources @@ -178,44 +180,6 @@ shared_examples_for "run_converge.failure payload check" do end end -shared_examples_for "node-update payload check" do - describe "node update message" do - let(:required_fields) do - %w{ - entity_name - entity_type - entity_uuid - id - message_type - message_version - organization_name - recorded_at - remote_hostname - requestor_name - requestor_type - run_id - service_hostname - source - task - user_agent - } - end - let(:optional_fields) { %{data} } - - it "is not missing any required fields" do - payload = JSON.load(command("curl http://localhost:9292/cache/action").stdout) - missing_fields = required_fields.select { |key| !payload.key?(key) } - expect(missing_fields).to eq([]) - end - - it "does not have any extra fields" do - payload = JSON.load(command("curl http://localhost:9292/cache/action").stdout) - extra_fields = payload.keys.select { |key| !required_fields.include?(key) && !optional_fields.include?(key) } - expect(extra_fields).to eq([]) - end - end -end - describe "CCR with no data collector URL configured" do include_examples "successful chef run", "chef-client -z -c /etc/chef/no-endpoint.rb" include_examples "counter checks", { "run_start" => nil, "run_converge.success" => nil, "run_converge.failure" => nil } @@ -226,7 +190,6 @@ describe "CCR, local mode, config in solo mode" do include_examples "counter checks", { "run_start" => 1, "run_converge.success" => 1, "run_converge.failure" => nil } include_examples "run_start payload check" include_examples "run_converge.success payload check" - include_examples "node-update payload check" end describe "CCR, local mode, config in client mode" do @@ -239,7 +202,6 @@ describe "CCR, local mode, config in both mode" do include_examples "counter checks", { "run_start" => 1, "run_converge.success" => 1, "run_converge.failure" => nil } include_examples "run_start payload check" include_examples "run_converge.success payload check" - include_examples "node-update payload check" end describe "CCR, local mode, config in solo mode, failed run" do @@ -247,5 +209,4 @@ describe "CCR, local mode, config in solo mode, failed run" do include_examples "counter checks", { "run_start" => 1, "run_converge.success" => nil, "run_converge.failure" => 1 } include_examples "run_start payload check" include_examples "run_converge.failure payload check" - include_examples "node-update payload check" end diff --git a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb index 203ea9809a..73f5151bca 100644 --- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb +++ b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb @@ -3,6 +3,8 @@ class TopCookbooks < Chef::Resource property :command, String, name_property: true + # Disabling all windows tests until winrm issue is properly settled. + # action :run do cookbook_kitchen "#{command} docker" do end @@ -33,8 +35,6 @@ class TopCookbooks < Chef::Resource repository "adamedx/winbox" end - # Temporarily disabling windows and chocolatey to eliminate - # transient errors on the builders # cookbook_kitchen "#{command} windows" do # end diff --git a/acceptance/trivial/.kitchen.yml b/acceptance/trivial/.kitchen.yml index 1e0af03503..0db67c468f 100644 --- a/acceptance/trivial/.kitchen.yml +++ b/acceptance/trivial/.kitchen.yml @@ -3,5 +3,5 @@ verifier: suites: - name: chef-current-install - includes: [windows-2012r2] + includes: [ubuntu-14.04, windows-server-2012r2] run_list: diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb index e2d663ac2f..e12f938cf3 100644 --- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb +++ b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb @@ -1 +1 @@ -kitchen "destroy" +#kitchen "destroy" diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb index 5726c0e7b5..cec9de4e5d 100644 --- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb +++ b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb @@ -1 +1 @@ -kitchen "converge" +#kitchen "converge" diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb index 05ac94ce66..52e3560cf6 100644 --- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb +++ b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb @@ -1 +1 @@ -kitchen "verify" +#kitchen "verify" diff --git a/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb b/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb index a791177362..75383b69bf 100644 --- a/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb +++ b/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb @@ -13,7 +13,7 @@ describe service("chef-client") do it { should_not be_running } end -describe command("chef-service-manager -a install") do +describe command("/opscode/chef/bin/chef-service-manager.bat -a install") do its("exit_status") { should eq 0 } its(:stdout) { should match /Service 'chef-client' has successfully been installed./ } end @@ -24,7 +24,7 @@ describe service("chef-client") do it { should_not be_running } end -describe command("chef-service-manager -a start") do +describe command("/opscode/chef/bin/chef-service-manager.bat -a start") do its("exit_status") { should eq 0 } its(:stdout) { should match /Service 'chef-client' is now 'running'/ } end @@ -35,7 +35,7 @@ describe service("chef-client") do it { should be_running } end -describe command("chef-service-manager -a stop") do +describe command("/opscode/chef/bin/chef-service-manager.bat -a stop") do its("exit_status") { should eq 0 } its(:stdout) { should match /Service 'chef-client' is now 'stopped'/ } end @@ -46,7 +46,7 @@ describe service("chef-client") do it { should_not be_running } end -describe command("chef-service-manager -a uninstall") do +describe command("/opscode/chef/bin/chef-service-manager.bat -a uninstall") do its("exit_status") { should eq 0 } its(:stdout) { should match /Service chef-client deleted/ } end diff --git a/chef-config/Rakefile b/chef-config/Rakefile index 46f87c96c9..fd6497a287 100644 --- a/chef-config/Rakefile +++ b/chef-config/Rakefile @@ -1,6 +1,6 @@ require "chef-config/package_task" -ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "ChefConfig") do |package| +ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "ChefConfig", "chef-config") do |package| package.module_path = "chef-config" end diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index 568467456f..a194edc80e 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -132,8 +132,8 @@ module ChefConfig until File.directory?(PathHelper.join(path, "cookbooks")) || File.directory?(PathHelper.join(path, "cookbook_artifacts")) new_path = File.expand_path("..", path) if new_path == path - ChefConfig.logger.warn("No cookbooks directory found at or above current directory. Assuming #{Dir.pwd}.") - return Dir.pwd + ChefConfig.logger.warn("No cookbooks directory found at or above current directory. Assuming #{cwd}.") + return cwd end path = new_path end @@ -519,7 +519,16 @@ module ChefConfig # Set to true if Chef is to set OpenSSL to run in FIPS mode default(:fips) do - !ENV["CHEF_FIPS"].nil? || ChefConfig.fips? + # CHEF_FIPS is used in testing to override checking for system level + # enablement. There are 3 possible values that this variable may have: + # nil - no override and the system will be checked + # empty - FIPS is NOT enabled + # a non empty value - FIPS is enabled + if ENV["CHEF_FIPS"] == "" + false + else + !ENV["CHEF_FIPS"].nil? || ChefConfig.fips? + end end # Initialize openssl diff --git a/chef-config/lib/chef-config/package_task.rb b/chef-config/lib/chef-config/package_task.rb index b984f60f9f..eb257ff4b5 100644 --- a/chef-config/lib/chef-config/package_task.rb +++ b/chef-config/lib/chef-config/package_task.rb @@ -31,6 +31,10 @@ module ChefConfig # the top level module which contains VERSION and MODULE_ROOT. attr_accessor :module_name + # Name of the gem being built. This is used to find the lines to fix in + # Gemfile.lock. + attr_accessor :gem_name + # Should the generated version.rb be in a class or module? Default is false (module). attr_accessor :generate_version_class @@ -55,15 +59,16 @@ module ChefConfig # Name of git remote used to push tags during a release. Default is origin. attr_accessor :git_remote - def initialize(root_path = nil, module_name = nil) - init(root_path, module_name) + def initialize(root_path = nil, module_name = nil, gem_name = nil) + init(root_path, module_name, gem_name) yield self if block_given? define unless root_path.nil? || module_name.nil? end - def init(root_path, module_name) + def init(root_path, module_name, gem_name) @root_path = root_path @module_name = module_name + @gem_name = gem_name @component_paths = [] @module_path = nil @package_dir = "pkg" @@ -87,6 +92,10 @@ module ChefConfig File.join(chef_root_path, "VERSION") end + def gemfile_lock_path + File.join(root_path, "Gemfile.lock") + end + def version IO.read(version_file_path).strip end @@ -155,6 +164,26 @@ module ChefConfig namespace :version do desc 'Regenerate lib/#{@module_path}/version.rb from VERSION file' task :update => :update_components_versions do + update_version_rb + update_gemfile_lock + end + + task :bump => %w{version:bump_patch version:update} + + task :show do + puts version + end + + # Add 1 to the current patch version in the VERSION file, and write it back out. + task :bump_patch do + current_version = version + new_version = current_version.sub(/^(\d+\.\d+\.)(\d+)/) { "#{$1}#{$2.to_i + 1}" } + puts "Updating version in #{version_rb_path} from #{current_version.chomp} to #{new_version.chomp}" + IO.write(version_file_path, new_version) + end + + def update_version_rb + puts "Updating #{version_rb_path} to include version #{version} ..." contents = <<-VERSION_RB # Copyright:: Copyright 2010-2016, Chef Software, Inc. # License:: Apache License, Version 2.0 @@ -194,18 +223,15 @@ end IO.write(version_rb_path, contents) end - task :bump => %w{version:bump_patch version:update} - - task :show do - puts version - end - - # Add 1 to the current patch version in the VERSION file, and write it back out. - task :bump_patch do - current_version = version - new_version = current_version.sub(/^(\d+\.\d+\.)(\d+)/) { "#{$1}#{$2.to_i + 1}" } - puts "Updating version in #{version_rb_path} from #{current_version.chomp} to #{new_version.chomp}" - IO.write(version_file_path, new_version) + def update_gemfile_lock + if File.exist?(gemfile_lock_path) + puts "Updating #{gemfile_lock_path} to include version #{version} ..." + contents = IO.read(gemfile_lock_path) + contents.gsub!(/^\s*(chef|chef-config)\s*\((= )?\S+\)\s*$/) do |line| + line.gsub(/\((= )?\d+(\.\d+)+/) { "(#{$1}#{version}" } + end + IO.write(gemfile_lock_path, contents) + end end end diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index 3b1cf7aa51..d428427cbe 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.12.2" + VERSION = "12.13.21" end # diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb index f09dbb517a..0ddb56cf0d 100644 --- a/chef-config/spec/unit/config_spec.rb +++ b/chef-config/spec/unit/config_spec.rb @@ -186,6 +186,16 @@ RSpec.describe ChefConfig::Config do expect(ChefConfig::Config[:fips]).to eq(false) end + context "when ENV['CHEF_FIPS'] is empty" do + before do + ENV["CHEF_FIPS"] = "" + end + + it "returns false" do + expect(ChefConfig::Config[:fips]).to eq(false) + end + end + context "when ENV['CHEF_FIPS'] is set" do before do ENV["CHEF_FIPS"] = "1" diff --git a/chef.gemspec b/chef.gemspec index b88c899d5c..14b31ca23e 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -17,10 +17,11 @@ Gem::Specification.new do |s| s.add_dependency "chef-config", "= #{Chef::VERSION}" - s.add_dependency "mixlib-cli", "~> 1.4" + s.add_dependency "mixlib-cli", "~> 1.4", "< 1.7" s.add_dependency "mixlib-log", "~> 1.3" s.add_dependency "mixlib-authentication", "~> 1.4" s.add_dependency "mixlib-shellout", "~> 2.0" + s.add_dependency "mixlib-archive", ">= 0.2.0" s.add_dependency "ohai", ">= 8.6.0.alpha.1", "< 9" s.add_dependency "ffi-yajl", "~> 2.2" @@ -31,13 +32,13 @@ Gem::Specification.new do |s| s.add_dependency "erubis", "~> 2.7" s.add_dependency "diff-lcs", "~> 1.2", ">= 1.2.4" - s.add_dependency "chef-zero", "~> 4.5" + s.add_dependency "chef-zero", "~> 4.8" s.add_dependency "plist", "~> 3.2" s.add_dependency "iniparse", "~> 1.4" # Audit mode requires these, so they are non-developmental dependencies now - %w{rspec-core rspec-expectations rspec-mocks}.each { |gem| s.add_dependency gem, "~> 3.4" } + %w{rspec-core rspec-expectations rspec-mocks}.each { |gem| s.add_dependency gem, "~> 3.5" } s.add_dependency "rspec_junit_formatter", "~> 0.2.0" s.add_dependency "serverspec", "~> 2.7" s.add_dependency "specinfra", "~> 2.10" diff --git a/kitchen-tests/.kitchen.travis.yml b/kitchen-tests/.kitchen.travis.yml index 99486928de..dd92b832dc 100644 --- a/kitchen-tests/.kitchen.travis.yml +++ b/kitchen-tests/.kitchen.travis.yml @@ -24,6 +24,7 @@ provisioner: verifier: name: inspec + format: progress platforms: - name: debian-7 diff --git a/kitchen-tests/.kitchen.yml b/kitchen-tests/.kitchen.yml index c02ea55a02..314857663e 100644 --- a/kitchen-tests/.kitchen.yml +++ b/kitchen-tests/.kitchen.yml @@ -7,6 +7,7 @@ driver: verifier: name: inspec + format: progress provisioner: name: chef_github diff --git a/kitchen-tests/Berksfile b/kitchen-tests/Berksfile index 31e49b3e18..407b685236 100644 --- a/kitchen-tests/Berksfile +++ b/kitchen-tests/Berksfile @@ -5,4 +5,4 @@ cookbook "base", path: "cookbooks/base" cookbook "php", "~> 1.5.0" -cookbook "resolver", github: "chef-cookbooks/resolver", branch: "lcg/docker" +cookbook "resolver", github: "chef-cookbooks/resolver" diff --git a/kitchen-tests/Berksfile.lock b/kitchen-tests/Berksfile.lock index f4a9de89e2..eeecf4954f 100644 --- a/kitchen-tests/Berksfile.lock +++ b/kitchen-tests/Berksfile.lock @@ -4,15 +4,15 @@ DEPENDENCIES php (~> 1.5.0) resolver git: git://github.com/chef-cookbooks/resolver.git - revision: dd65ab8e2346cc0739c13682c74868f5b939b06a - branch: lcg/docker + revision: 8bf9034dabc47d29a07870e4059c32114f2c820a webapp path: cookbooks/webapp GRAPH apache2 (3.2.2) - apt (3.0.0) - aws (3.3.3) + apt (4.0.1) + compat_resource (>= 12.10) + aws (3.4.0) ohai (>= 2.1.0) base (0.1.0) apt (>= 0.0.0) @@ -30,18 +30,19 @@ GRAPH ubuntu (>= 0.0.0) users (>= 0.0.0) yum-epel (>= 0.0.0) - build-essential (4.0.0) - mingw (>= 0.0.0) + build-essential (6.0.0) + compat_resource (>= 12.10) + mingw (>= 1.1) seven_zip (>= 0.0.0) - chef-client (4.5.2) + chef-client (4.6.0) cron (>= 1.7.0) logrotate (>= 1.9.0) - windows (>= 1.39.0) + windows (>= 1.42.0) chef-sugar (3.3.0) chef_handler (1.4.0) chef_hostname (0.4.1) compat_resource (>= 0.0.0) - compat_resource (12.10.4) + compat_resource (12.10.6) cron (1.7.6) database (2.3.1) aws (>= 0.0.0) @@ -49,11 +50,11 @@ GRAPH mysql-chef_gem (~> 0.0) postgresql (>= 1.0.0) xfs (>= 0.0.0) - iis (4.1.7) + iis (4.1.10) windows (>= 1.34.6) iptables (2.2.0) logrotate (1.9.2) - mingw (1.0.0) + mingw (1.2.2) compat_resource (>= 0.0.0) seven_zip (>= 0.0.0) multipackage (3.0.28) @@ -63,12 +64,12 @@ GRAPH mysql-chef_gem (0.0.5) build-essential (>= 0.0.0) mysql (>= 0.0.0) - nscd (4.0.0) + nscd (4.1.0) compat_resource (>= 0.0.0) ntp (2.0.0) windows (>= 1.38.0) - ohai (4.0.2) - compat_resource (>= 12.9.0) + ohai (4.1.1) + compat_resource (>= 12.10) openssh (2.0.0) iptables (>= 1.0) openssl (4.4.0) @@ -97,13 +98,13 @@ GRAPH database (~> 2.3.1) mysql (~> 5.6.3) php (~> 1.5.0) - windows (1.41.0) + windows (1.44.1) chef_handler (>= 0.0.0) xfs (2.0.1) xml (2.0.0) build-essential (>= 0.0.0) chef-sugar (>= 0.0.0) - yum (3.10.0) + yum (3.11.0) yum-epel (0.7.0) yum (>= 3.6.3) yum-mysql-community (0.2.0) diff --git a/kitchen-tests/Gemfile.lock b/kitchen-tests/Gemfile.lock index 8ab7fbe707..edc91cc8b2 100644 --- a/kitchen-tests/Gemfile.lock +++ b/kitchen-tests/Gemfile.lock @@ -2,14 +2,14 @@ GEM remote: https://rubygems.org/ specs: addressable (2.4.0) - artifactory (2.3.2) - aws-sdk (2.3.9) - aws-sdk-resources (= 2.3.9) - aws-sdk-core (2.3.9) + artifactory (2.3.3) + aws-sdk (2.3.21) + aws-sdk-resources (= 2.3.21) + aws-sdk-core (2.3.21) jmespath (~> 1.0) - aws-sdk-resources (2.3.9) - aws-sdk-core (= 2.3.9) - berkshelf (4.3.3) + aws-sdk-resources (2.3.21) + aws-sdk-core (= 2.3.21) + berkshelf (4.3.5) addressable (~> 2.3, >= 2.3.4) berkshelf-api-client (~> 2.0, >= 2.0.2) buff-config (~> 1.0) @@ -21,6 +21,7 @@ GEM faraday (~> 0.9) httpclient (~> 2.7) minitar (~> 0.5, >= 0.5.4) + mixlib-archive (~> 0.1) octokit (~> 4.0) retryable (~> 2.0) ridley (~> 4.5) @@ -44,21 +45,21 @@ GEM celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.10.24) + chef-config (12.12.15) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) cleanroom (1.0.0) coderay (1.1.1) diff-lcs (1.2.5) - docker-api (1.26.2) + docker-api (1.29.0) excon (>= 0.38.0) json erubis (2.7.0) - excon (0.49.0) + excon (0.51.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) - ffi (1.9.10) + ffi (1.9.14) fuzzyurl (0.8.0) gssapi (1.2.0) ffi (>= 1.0.1) @@ -66,26 +67,23 @@ GEM builder (>= 2.1.2) hashie (3.4.4) hitimes (1.2.4) - hitimes (1.2.4-x86-mingw32) httpclient (2.7.2) - inspec (0.22.1) + inspec (0.27.0) hashie (~> 3.4) json (~> 1.8) method_source (~> 0.8) pry (~> 0) - r-train (~> 0.12) rainbow (~> 2) rspec (~> 3) rspec-its (~> 1.2) rubyzip (~> 1.1) thor (~> 0.19) - jmespath (1.2.4) - json_pure (>= 1.8.1) + train (>= 0.15.1, < 1.0) + jmespath (1.3.0) json (1.8.3) - json_pure (1.8.3) kitchen-appbundle-updater (0.1.2) - kitchen-dokken (0.0.29) - docker-api (~> 1.26.2) + kitchen-dokken (0.0.31) + docker-api (~> 1.29) test-kitchen (~> 1.5) kitchen-ec2 (1.0.0) aws-sdk (~> 2) @@ -104,44 +102,32 @@ GEM multi_json (~> 1.10) method_source (0.8.2) minitar (0.5.4) - mixlib-authentication (1.4.0) + mixlib-archive (0.2.0) + mixlib-log + mixlib-authentication (1.4.1) mixlib-log - rspec-core (~> 3.2) - rspec-expectations (~> 3.2) - rspec-mocks (~> 3.2) mixlib-config (2.2.1) - mixlib-install (1.0.12) + mixlib-install (1.1.0) artifactory mixlib-shellout mixlib-versioning mixlib-log (1.6.0) mixlib-shellout (2.2.6) - mixlib-shellout (2.2.6-universal-mingw32) - win32-process (~> 0.8.2) - wmi-lite (~> 1.0) mixlib-versioning (1.1.0) molinillo (0.4.5) multi_json (1.12.1) multipart-post (2.0.0) net-scp (1.2.1) net-ssh (>= 2.6.5) - net-ssh (3.1.1) + net-ssh (3.2.0) nio4r (1.2.1) nori (2.6.0) octokit (4.3.0) sawyer (~> 0.7.0, >= 0.5.3) - pry (0.10.3) + pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - r-train (0.12.1) - docker-api (~> 1.26) - json (~> 1.8) - mixlib-shellout (~> 2.0) - net-scp (~> 1.2) - net-ssh (>= 2.9, < 4.0) - winrm (~> 1.6) - winrm-fs (~> 0.3) rainbow (2.1.0) retryable (2.0.3) ridley (4.5.1) @@ -162,22 +148,22 @@ GEM retryable (~> 2.0) semverse (~> 1.1) varia_model (~> 0.4.0) - rspec (3.4.0) - rspec-core (~> 3.4.0) - rspec-expectations (~> 3.4.0) - rspec-mocks (~> 3.4.0) - rspec-core (3.4.4) - rspec-support (~> 3.4.0) - rspec-expectations (3.4.0) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.1) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) + rspec-support (~> 3.5.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.4.1) + rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) - rspec-support (3.4.1) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) rubyntlm (0.6.0) rubyzip (1.2.0) safe_yaml (1.0.4) @@ -189,7 +175,7 @@ GEM solve (2.0.3) molinillo (~> 0.4.2) semverse (~> 1.1) - test-kitchen (1.9.0) + test-kitchen (1.10.2) mixlib-install (~> 1.0, >= 1.0.4) mixlib-shellout (>= 1.2, < 3.0) net-scp (~> 1.1) @@ -199,12 +185,18 @@ GEM thor (0.19.1) timers (4.0.4) hitimes + train (0.15.1) + docker-api (~> 1.26) + json (~> 1.8) + mixlib-shellout (~> 2.0) + net-scp (~> 1.2) + net-ssh (>= 2.9, < 4.0) + winrm (~> 1.6) + winrm-fs (~> 0.3) vagrant-wrapper (2.0.3) varia_model (0.4.1) buff-extensions (~> 1.0) hashie (>= 2.0.2, < 4.0.0) - win32-process (0.8.3) - ffi (>= 1.0.0) winrm (1.8.1) builder (>= 2.1.2) gssapi (~> 1.2) @@ -213,16 +205,14 @@ GEM logging (>= 1.6.1, < 3.0) nori (~> 2.0) rubyntlm (~> 0.6.0) - winrm-fs (0.4.2) + winrm-fs (0.4.3) erubis (~> 2.7) logging (>= 1.6.1, < 3.0) rubyzip (~> 1.1) winrm (~> 1.5) - wmi-lite (1.0.0) PLATFORMS ruby - x86-mingw32 DEPENDENCIES berkshelf diff --git a/lib/chef/application.rb b/lib/chef/application.rb index fa740e068a..c377ffdb36 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -29,6 +29,10 @@ require "tmpdir" require "rbconfig" require "chef/application/exit_code" require "yaml" +require "resolv" +# on linux, we replace the glibc resolver with the ruby resolv library, which +# supports reloading. +require "resolv-replace" if RbConfig::CONFIG["host_os"] =~ /linux/ class Chef class Application diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index cec47ac071..71bb300971 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -27,6 +27,7 @@ require "chef/handler/error_report" require "chef/workstation_config_loader" require "chef/mixin/shell_out" require "chef-config/mixin/dot_d" +require "mixlib/archive" class Chef::Application::Client < Chef::Application include Chef::Mixin::ShellOut @@ -342,8 +343,7 @@ class Chef::Application::Client < Chef::Application FileUtils.mkdir_p(Chef::Config.chef_repo_path) tarball_path = File.join(Chef::Config.chef_repo_path, "recipes.tgz") fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path) - result = shell_out!("tar zxvf #{tarball_path} -C #{Chef::Config.chef_repo_path}") - Chef::Log.debug "#{result.stdout}" + Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/) end end diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb index d38e97e82b..8bf381a975 100644 --- a/lib/chef/application/solo.rb +++ b/lib/chef/application/solo.rb @@ -29,6 +29,7 @@ require "fileutils" require "chef/mixin/shell_out" require "pathname" require "chef-config/mixin/dot_d" +require "mixlib/archive" class Chef::Application::Solo < Chef::Application include Chef::Mixin::ShellOut @@ -249,6 +250,13 @@ class Chef::Application::Solo < Chef::Application ARGV[dash_r] = "--recipe-url" end + # For back compat reasons, we need to ensure that we try and use the cache_path as a repo first + Chef::Log.debug "Current chef_repo_path is #{Chef::Config.chef_repo_path}" + + if !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path) + Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Chef::Config[:cache_path]) + end + Chef::Config[:local_mode] = true else configure_legacy_mode! @@ -274,8 +282,7 @@ class Chef::Application::Solo < Chef::Application FileUtils.mkdir_p(recipes_path) tarball_path = File.join(recipes_path, "recipes.tgz") fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path) - result = shell_out!("tar zxvf #{tarball_path} -C #{recipes_path}") - Chef::Log.debug "#{result.stdout}" + Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/) end # json_attribs shuld be fetched after recipe_url tarball is unpacked. diff --git a/lib/chef/audit/audit_reporter.rb b/lib/chef/audit/audit_reporter.rb index a40cae93dd..8546a21bb4 100644 --- a/lib/chef/audit/audit_reporter.rb +++ b/lib/chef/audit/audit_reporter.rb @@ -140,7 +140,11 @@ class Chef # Save the audit report to local disk error_file = "failed-audit-data.json" Chef::FileCache.store(error_file, Chef::JSONCompat.to_json_pretty(run_data), 0640) - Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}") + if Chef::Config.chef_zero.enabled + Chef::Log.debug("Saving audit report to #{Chef::FileCache.load(error_file, false)}") + else + Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}") + end end else Chef::Log.error("Failed to post audit report to server (#{e})") diff --git a/lib/chef/audit/runner.rb b/lib/chef/audit/runner.rb index 100a72d2e1..837346381c 100644 --- a/lib/chef/audit/runner.rb +++ b/lib/chef/audit/runner.rb @@ -165,7 +165,7 @@ class Chef add_example_group_methods run_context.audits.each do |name, group| ctl_grp = RSpec::Core::ExampleGroup.__control_group__(*group.args, &group.block) - RSpec.world.register(ctl_grp) + RSpec.world.record(ctl_grp) end end diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb index aa5a6d5a69..6b3e830f8d 100644 --- a/lib/chef/chef_fs/chef_fs_data_store.rb +++ b/lib/chef/chef_fs/chef_fs_data_store.rb @@ -458,6 +458,7 @@ class Chef # We want to delete just the ones that == POLICY next unless policy.name.rpartition("-")[0] == path[1] policy.delete(false) + FileSystemCache.instance.delete!(policy.file_path) found_policy = true end raise ChefZero::DataStore::DataNotFoundError.new(path) if !found_policy diff --git a/lib/chef/chef_fs/file_system/multiplexed_dir.rb b/lib/chef/chef_fs/file_system/multiplexed_dir.rb index d3951edd51..21abc012f8 100644 --- a/lib/chef/chef_fs/file_system/multiplexed_dir.rb +++ b/lib/chef/chef_fs/file_system/multiplexed_dir.rb @@ -41,7 +41,7 @@ class Chef child_entry = dir.child(name) if child_entry.exists? if result - Chef::Log.debug("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}") unless seen[child.name].path_for_printing == child.path_for_printing + Chef::Log.debug("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}") else result = child_entry end diff --git a/lib/chef/chef_fs/file_system/repository/acls_dir.rb b/lib/chef/chef_fs/file_system/repository/acls_dir.rb index 619031aa70..110befdf22 100644 --- a/lib/chef/chef_fs/file_system/repository/acls_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/acls_dir.rb @@ -28,14 +28,16 @@ class Chef module Repository class AclsDir < Repository::Directory + BARE_FILES = %w{ organization.json root } + def can_have_child?(name, is_dir) - is_dir ? Chef::ChefFS::FileSystem::ChefServer::AclsDir::ENTITY_TYPES.include?(name) : name == "organization.json" + is_dir ? Chef::ChefFS::FileSystem::ChefServer::AclsDir::ENTITY_TYPES.include?(name) : BARE_FILES.include?(name) end protected def make_child_entry(child_name) - if child_name == "organization.json" + if BARE_FILES.include? child_name Acl.new(child_name, self) else AclsSubDir.new(child_name, self) diff --git a/lib/chef/chef_fs/file_system/repository/base_file.rb b/lib/chef/chef_fs/file_system/repository/base_file.rb index a768bcf971..3e1edc8d62 100644 --- a/lib/chef/chef_fs/file_system/repository/base_file.rb +++ b/lib/chef/chef_fs/file_system/repository/base_file.rb @@ -16,6 +16,8 @@ # limitations under the License. # +require "chef/chef_fs/file_system_cache" + class Chef module ChefFS module FileSystem @@ -99,6 +101,7 @@ class Chef end def delete(_) + FileSystemCache.instance.delete!(file_path) File.delete(file_path) rescue Errno::ENOENT raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb index 9d1538e46e..4019c6985b 100644 --- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb +++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb @@ -120,6 +120,7 @@ class Chef end def delete(recurse) + FileSystemCache.instance.delete!(file_path) begin if dir? if !recurse diff --git a/lib/chef/chef_fs/file_system/repository/directory.rb b/lib/chef/chef_fs/file_system/repository/directory.rb index dae467993a..328cf92b03 100644 --- a/lib/chef/chef_fs/file_system/repository/directory.rb +++ b/lib/chef/chef_fs/file_system/repository/directory.rb @@ -16,6 +16,8 @@ # limitations under the License. # +require "chef/chef_fs/file_system_cache" + class Chef module ChefFS module FileSystem @@ -68,9 +70,11 @@ class Chef end def children - dir_ls.sort. + return FileSystemCache.instance.children(file_path) if FileSystemCache.instance.exist?(file_path) + children = dir_ls.sort. map { |child_name| make_child_entry(child_name) }. select { |new_child| new_child.fs_entry_valid? && can_have_child?(new_child.name, new_child.dir?) } + FileSystemCache.instance.set_children(file_path, children) rescue Errno::ENOENT => e raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e) end @@ -80,6 +84,7 @@ class Chef if child.exists? raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, child) end + FileSystemCache.instance.delete!(child.file_path) if file_contents child.write(file_contents) else @@ -118,6 +123,7 @@ class Chef raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self) end begin + FileSystemCache.instance.delete!(file_path) Dir.mkdir(file_path) rescue Errno::EEXIST raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self) @@ -134,6 +140,7 @@ class Chef raise MustDeleteRecursivelyError.new(self, $!) end FileUtils.rm_r(file_path) + FileSystemCache.instance.delete!(file_path) else raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end @@ -145,6 +152,10 @@ class Chef protected + def write(data) + raise FileSystemError.new(self, nil, "attempted to write to a directory entry") + end + def make_child_entry(child_name) raise "Not Implemented" end diff --git a/lib/chef/chef_fs/file_system_cache.rb b/lib/chef/chef_fs/file_system_cache.rb new file mode 100644 index 0000000000..a9d8d8bfe4 --- /dev/null +++ b/lib/chef/chef_fs/file_system_cache.rb @@ -0,0 +1,80 @@ +# +# 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 "singleton" +require "chef/client" + +class Chef + module ChefFS + class FileSystemCache + include Singleton + + def initialize + @cache = {} + + Chef::Client.when_run_starts do + FileSystemCache.instance.reset! + end + end + + def reset! + @cache = {} + end + + def exist?(path) + @cache.key?(path) + end + + def children(path) + @cache[path]["children"] + end + + def set_children(path, val) + @cache[path] ||= { "children" => [] } + @cache[path]["children"] = val + val + end + + def delete!(path) + parent = _get_parent(path) + Chef::Log.debug("Deleting parent #{parent} and #{path} from FileSystemCache") + if @cache.key?(path) + @cache.delete(path) + end + if !parent.nil? && @cache.key?(parent) + @cache.delete(parent) + end + end + + def fetch(path) + if @cache.key?(path) + @cache[path] + else + false + end + end + + private + + def _get_parent(path) + parts = ChefFS::PathUtils.split(path) + return nil if parts.nil? || parts.length < 2 + ChefFS::PathUtils.join(*parts[0..-2]) + end + end + end +end diff --git a/lib/chef/data_collector.rb b/lib/chef/data_collector.rb index c8dd354f60..dbb0b3771a 100644 --- a/lib/chef/data_collector.rb +++ b/lib/chef/data_collector.rb @@ -22,6 +22,7 @@ require "uri" require "chef/event_dispatch/base" require "chef/data_collector/messages" require "chef/data_collector/resource_report" +require "ostruct" class Chef @@ -51,12 +52,14 @@ class Chef # and exports its data through a webhook-like mechanism to a configured # endpoint. class Reporter < EventDispatch::Base - attr_reader :completed_resources, :status, :exception, :error_descriptions, - :expanded_run_list, :run_status, :http, + attr_reader :all_resource_reports, :status, :exception, :error_descriptions, + :expanded_run_list, :run_context, :run_status, :http, :current_resource_report, :enabled def initialize - @completed_resources = [] + validate_data_collector_server_url! + + @all_resource_reports = [] @current_resource_loaded = nil @error_descriptions = {} @expanded_run_list = {} @@ -93,6 +96,29 @@ class Chef send_run_completion(status: "failure") end + # see EventDispatch::Base#converge_start + # Upon receipt, we stash the run_context for use at the + # end of the run in order to determine what resource+action + # combinations have not yet fired so we can report on + # unprocessed resources. + def converge_start(run_context) + @run_context = run_context + end + + # see EventDispatch::Base#converge_complete + # At the end of the converge, we add any unprocessed resources + # to our report list. + def converge_complete + detect_unprocessed_resources + end + + # see EventDispatch::Base#converge_failed + # At the end of the converge, we add any unprocessed resources + # to our report list + def converge_failed(exception) + detect_unprocessed_resources + end + # see EventDispatch::Base#resource_current_state_loaded # Create a new ResourceReport instance that we'll use to track # the state of this resource during the run. Nested resources are @@ -100,13 +126,7 @@ class Chef # resource, and we only care about tracking top-level resources. def resource_current_state_loaded(new_resource, action, current_resource) return if nested_resource?(new_resource) - update_current_resource_report( - Chef::DataCollector::ResourceReport.new( - new_resource, - action, - current_resource - ) - ) + update_current_resource_report(create_resource_report(new_resource, action, current_resource)) end # see EventDispatch::Base#resource_up_to_date @@ -116,19 +136,15 @@ class Chef end # see EventDispatch::Base#resource_skipped - # If this is a top-level resource, we create a ResourceReport instance - # (because a skipped resource does not trigger the + # If this is a top-level resource, we create a ResourceReport + # instance (because a skipped resource does not trigger the # resource_current_state_loaded event), and flag it as skipped. def resource_skipped(new_resource, action, conditional) return if nested_resource?(new_resource) - update_current_resource_report( - Chef::DataCollector::ResourceReport.new( - new_resource, - action - ) - ) - current_resource_report.skipped(conditional) + resource_report = create_resource_report(new_resource, action) + resource_report.skipped(conditional) + update_current_resource_report(resource_report) end # see EventDispatch::Base#resource_updated @@ -154,13 +170,12 @@ class Chef end # see EventDispatch::Base#resource_completed - # Mark the ResourceReport instance as finished (for timing details) - # and add it to the list of resources encountered during this run. + # Mark the ResourceReport instance as finished (for timing details). # This marks the end of this resource during this run. def resource_completed(new_resource) if current_resource_report && !nested_resource?(new_resource) current_resource_report.finish - add_completed_resource(current_resource_report) + add_resource_report(current_resource_report) update_current_resource_report(nil) end end @@ -223,7 +238,8 @@ class Chef yield rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse, - Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError => e + Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError, + Errno::EHOSTDOWN => e disable_data_collector_reporter code = if e.respond_to?(:response) && e.response.code e.response.code.to_s @@ -266,12 +282,11 @@ class Chef # we have nothing to report. return unless run_status - send_to_data_collector(Chef::DataCollector::Messages.node_update_message(run_status).to_json) send_to_data_collector( Chef::DataCollector::Messages.run_end_message( run_status: run_status, expanded_run_list: expanded_run_list, - completed_resources: completed_resources, + resources: all_resource_reports, status: opts[:status], error_descriptions: error_descriptions ).to_json @@ -297,8 +312,12 @@ class Chef Chef::Config[:data_collector][:token] end - def add_completed_resource(resource_report) - @completed_resources << resource_report + def add_resource_report(resource_report) + @all_resource_reports << OpenStruct.new( + resource: resource_report.new_resource, + action: resource_report.action, + report_data: resource_report.to_hash + ) end def disable_data_collector_reporter @@ -321,6 +340,38 @@ class Chef @error_descriptions = discription_hash end + def create_resource_report(new_resource, action, current_resource = nil) + Chef::DataCollector::ResourceReport.new( + new_resource, + action, + current_resource + ) + end + + def detect_unprocessed_resources + # create a Set containing all resource+action combinations from + # the Resource Collection + collection_resources = Set.new + run_context.resource_collection.all_resources.each do |resource| + Array(resource.action).each do |action| + collection_resources.add([resource, action]) + end + end + + # Delete from the Set any resource+action combination we have + # already processed. + all_resource_reports.each do |report| + collection_resources.delete([report.resource, report.action]) + end + + # The items remaining in the Set are unprocessed resource+actions, + # so we'll create new resource reports for them which default to + # a state of "unprocessed". + collection_resources.each do |resource, action| + add_resource_report(create_resource_report(resource, action)) + end + end + # If we are getting messages about a resource while we are in the middle of # another resource's update, we assume that the nested resource is just the # implementation of a provider, and we want to hide it from the reporting @@ -328,6 +379,20 @@ class Chef def nested_resource?(new_resource) @current_resource_report && @current_resource_report.new_resource != new_resource end + + def validate_data_collector_server_url! + raise Chef::Exceptions::ConfigurationError, + "Chef::Config[:data_collector][:server_url] is empty. Please supply a valid URL." if data_collector_server_url.empty? + + begin + uri = URI(data_collector_server_url) + rescue URI::InvalidURIError + raise Chef::Exceptions::ConfigurationError, "Chef::Config[:data_collector][:server_url] (#{data_collector_server_url}) is not a valid URI." + end + + raise Chef::Exceptions::ConfigurationError, + "Chef::Config[:data_collector][:server_url] (#{data_collector_server_url}) is a URI with no host. Please supply a valid URL." if uri.host.nil? + end end end end diff --git a/lib/chef/data_collector/messages.rb b/lib/chef/data_collector/messages.rb index e23262c9e4..8c2a84b580 100644 --- a/lib/chef/data_collector/messages.rb +++ b/lib/chef/data_collector/messages.rb @@ -68,17 +68,18 @@ class Chef "id" => run_status.run_id, "message_version" => "1.0.0", "message_type" => "run_converge", + "node" => run_status.node, "node_name" => run_status.node.name, "organization_name" => organization, - "resources" => reporter_data[:completed_resources].map(&:for_json), + "resources" => reporter_data[:resources].map(&:report_data), "run_id" => run_status.run_id, "run_list" => run_status.node.run_list.for_json, "start_time" => run_status.start_time.utc.iso8601, "end_time" => run_status.end_time.utc.iso8601, "source" => collector_source, "status" => reporter_data[:status], - "total_resource_count" => reporter_data[:completed_resources].count, - "updated_resource_count" => reporter_data[:completed_resources].select { |r| r.status == "updated" }.count, + "total_resource_count" => reporter_data[:resources].count, + "updated_resource_count" => reporter_data[:resources].select { |r| r.report_data["status"] == "updated" }.count, } message["error"] = { @@ -90,36 +91,6 @@ class Chef message end - - # - # Message payload that is sent to the DataCollector server at the - # end of a Chef run. - # - # @param run_status [Chef::RunStatus] The RunStatus instance for this node/run. - # - # @return [Hash] A hash containing the node object and related metadata. - # - def self.node_update_message(run_status) - { - "entity_name" => run_status.node.name, - "entity_type" => "node", - "entity_uuid" => node_uuid, - "id" => SecureRandom.uuid, - "message_version" => "1.1.0", - "message_type" => "action", - "organization_name" => organization, - "recorded_at" => Time.now.utc.iso8601, - "remote_hostname" => run_status.node["fqdn"], - "requestor_name" => run_status.node.name, - "requestor_type" => "client", - "run_id" => run_status.run_id, - "service_hostname" => chef_server_fqdn(run_status), - "source" => collector_source, - "task" => "update", - "user_agent" => Chef::HTTP::HTTPRequest::DEFAULT_UA, - "data" => run_status.node, - } - end end end end diff --git a/lib/chef/data_collector/messages/helpers.rb b/lib/chef/data_collector/messages/helpers.rb index 3e52f80047..c0c700f847 100644 --- a/lib/chef/data_collector/messages/helpers.rb +++ b/lib/chef/data_collector/messages/helpers.rb @@ -148,8 +148,8 @@ class Chef end def update_metadata(key, value) - metadata[key] = value - Chef::FileCache.store(metadata_filename, metadata.to_json, 0644) + updated_metadata = metadata.tap { |x| x[key] = value } + Chef::FileCache.store(metadata_filename, updated_metadata.to_json, 0644) end def metadata_filename diff --git a/lib/chef/data_collector/resource_report.rb b/lib/chef/data_collector/resource_report.rb index 1793fe2c9d..dcaf9c8e44 100644 --- a/lib/chef/data_collector/resource_report.rb +++ b/lib/chef/data_collector/resource_report.rb @@ -22,13 +22,14 @@ class Chef class DataCollector class ResourceReport - attr_reader :action, :current_resource, :elapsed_time, :new_resource, :status - attr_accessor :conditional, :exception + attr_reader :action, :elapsed_time, :new_resource, :status + attr_accessor :conditional, :current_resource, :exception def initialize(new_resource, action, current_resource = nil) @new_resource = new_resource @action = action @current_resource = current_resource + @status = "unprocessed" end def skipped(conditional) @@ -54,22 +55,32 @@ class Chef @elapsed_time = new_resource.elapsed_time end + def elapsed_time_in_milliseconds + elapsed_time.nil? ? nil : (elapsed_time * 1000).to_i + end + + def potentially_changed? + %w{updated failed}.include?(status) + end + def to_hash hash = { - "type" => new_resource.resource_name.to_sym, - "name" => new_resource.name.to_s, - "id" => new_resource.identity.to_s, - "after" => new_resource.state_for_resource_reporter, - "before" => current_resource ? current_resource.state_for_resource_reporter : {}, - "duration" => (elapsed_time * 1000).to_i.to_s, - "delta" => new_resource.respond_to?(:diff) ? new_resource.diff : "", - "result" => action.to_s, - "status" => status, + "type" => new_resource.resource_name.to_sym, + "name" => new_resource.name.to_s, + "id" => new_resource.identity.to_s, + "after" => new_resource.state_for_resource_reporter, + "before" => current_resource ? current_resource.state_for_resource_reporter : {}, + "duration" => elapsed_time_in_milliseconds.to_s, + "delta" => new_resource.respond_to?(:diff) && potentially_changed? ? new_resource.diff : "", + "ignore_failure" => new_resource.ignore_failure, + "result" => action.to_s, + "status" => status, } if new_resource.cookbook_name hash["cookbook_name"] = new_resource.cookbook_name hash["cookbook_version"] = new_resource.cookbook_version.version + hash["recipe_name"] = new_resource.recipe_name end hash["conditional"] = conditional.to_text if status == "skipped" diff --git a/lib/chef/decorator/unchain.rb b/lib/chef/decorator/unchain.rb new file mode 100644 index 0000000000..8093c70f4c --- /dev/null +++ b/lib/chef/decorator/unchain.rb @@ -0,0 +1,59 @@ +class Chef + class Decorator < SimpleDelegator + # + # This decorator unchains method call chains and turns them into method calls + # with variable args. So this: + # + # node.set_unless["foo"]["bar"] = "baz" + # + # Can become: + # + # node.set_unless("foo", "bar", "baz") + # + # While this is a decorator it is not a Decorator and does not inherit because + # it deliberately does not need or want the method_missing magic. It is not legal + # to call anything on the intermediate values and only supports method chaining with + # #[] until the chain comes to an end with #[]=, so does not behave like a hash or + # array... e.g. + # + # node.default['foo'].keys is legal + # node.set_unless['foo'].keys is not legal now or ever + # + class Unchain + attr_accessor :__path__ + attr_accessor :__method__ + + def initialize(obj, method) + @__path__ = [] + @__method__ = method + @delegate_sd_obj = obj + end + + def [](key) + __path__.push(key) + self + end + + def []=(key, value) + __path__.push(key) + @delegate_sd_obj.public_send(__method__, *__path__, value) + end + + # unfortunately we have to support method_missing for node.set_unless.foo.bar = 'baz' notation + def method_missing(symbol, *args) + if symbol == :to_ary + merged_attributes.send(symbol, *args) + elsif args.empty? + Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])} + self[symbol] + elsif symbol.to_s =~ /=$/ + Chef.log_deprecation %q{method setting of node attributes (node.foo="bar") is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]="bar")} + key_to_set = symbol.to_s[/^(.+)=$/, 1] + self[key_to_set] = (args.length == 1 ? args[0] : args) + else + raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'" + end + end + end + end +end diff --git a/lib/chef/dsl/cheffish.rb b/lib/chef/dsl/cheffish.rb index de052bbe5c..03290b3674 100644 --- a/lib/chef/dsl/cheffish.rb +++ b/lib/chef/dsl/cheffish.rb @@ -27,6 +27,7 @@ class Chef chef_acl chef_client chef_container + chef_data_bag_item chef_data_bag chef_environment chef_group diff --git a/lib/chef/dsl/declare_resource.rb b/lib/chef/dsl/declare_resource.rb index 8d76ddfb31..86227a0f9d 100644 --- a/lib/chef/dsl/declare_resource.rb +++ b/lib/chef/dsl/declare_resource.rb @@ -71,7 +71,15 @@ class Chef # delete_resource!(:template, '/x/y.txy') # def delete_resource!(type, name, run_context: self.run_context) - run_context.resource_collection.delete("#{type}[#{name}]") + run_context.resource_collection.delete("#{type}[#{name}]").tap do |resource| + # Purge any pending notifications too. This will not raise an exception + # if there are no notifications. + if resource + run_context.before_notification_collection.delete(resource.declared_key) + run_context.immediate_notification_collection.delete(resource.declared_key) + run_context.delayed_notification_collection.delete(resource.declared_key) + end + end end # Lookup a resource in the resource collection by name and delete it. Returns diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index ea90d80cd8..43759568a7 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -106,7 +106,12 @@ class Chef # for back compat, need to raise an error that inherits from ArgumentError class CookbookNotFoundInRepo < ArgumentError; end class RecipeNotFound < ArgumentError; end + # AttributeNotFound really means the attribute file could not be found class AttributeNotFound < RuntimeError; end + # NoSuchAttribute is raised on access by node.read!("foo", "bar") when node["foo"]["bar"] does not exist. + class NoSuchAttribute < RuntimeError; end + # AttributeTypeMismatch is raised by node.write!("foo", "bar", "baz") when e.g. node["foo"] = "bar" (overwriting String with Hash) + class AttributeTypeMismatch < RuntimeError; end class MissingCookbookDependency < StandardError; end # CHEF-5120 class InvalidCommandOption < RuntimeError; end class CommandTimeout < RuntimeError; end diff --git a/lib/chef/http.rb b/lib/chef/http.rb index c6afa97d23..3e69f58383 100644 --- a/lib/chef/http.rb +++ b/lib/chef/http.rb @@ -232,11 +232,11 @@ class Chef # PERFORMANCE CRITICAL: *MUST* lazy require here otherwise we load up webrick # via chef-zero and that hits DNS (at *require* time) which may timeout, # when for most knife/chef-client work we never need/want this loaded. - Thread.exclusive { - unless defined?(SocketlessChefZeroClient) - require "chef/http/socketless_chef_zero_client" - end - } + + unless defined?(SocketlessChefZeroClient) + require "chef/http/socketless_chef_zero_client" + end + SocketlessChefZeroClient.new(base_url) else BasicClient.new(base_url, :ssl_policy => Chef::HTTP::APISSLPolicy) diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb index f5dc29371f..ee4d9ce7af 100644 --- a/lib/chef/knife/bootstrap.rb +++ b/lib/chef/knife/bootstrap.rb @@ -101,6 +101,14 @@ class Chef :description => "The proxy server for the node being bootstrapped", :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p } + option :bootstrap_proxy_user, + :long => "--bootstrap-proxy-user PROXY_USER", + :description => "The proxy authentication username for the node being bootstrapped" + + option :bootstrap_proxy_pass, + :long => "--bootstrap-proxy-pass PROXY_PASS", + :description => "The proxy authentication password for the node being bootstrapped" + option :bootstrap_no_proxy, :long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]", :description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode", @@ -224,6 +232,7 @@ class Chef unless valid_values.include?(v) raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}" end + v } option :node_verify_api_cert, diff --git a/lib/chef/knife/cookbook_create.rb b/lib/chef/knife/cookbook_create.rb index 950de380f8..ccb78bb7a6 100644 --- a/lib/chef/knife/cookbook_create.rb +++ b/lib/chef/knife/cookbook_create.rb @@ -56,6 +56,10 @@ class Chef :description => "Email address of cookbook maintainer" def run + Chef::Log.deprecation <<EOF +This command is being deprecated in favor of `chef generate cookbook` and will soon return an error. +Please use `chef generate cookbook` instead of this command. +EOF self.config = Chef::Config.merge!(config) if @name_args.length < 1 show_usage diff --git a/lib/chef/knife/cookbook_site_download.rb b/lib/chef/knife/cookbook_site_download.rb index 7d0e21791d..43677cfa78 100644 --- a/lib/chef/knife/cookbook_site_download.rb +++ b/lib/chef/knife/cookbook_site_download.rb @@ -37,6 +37,13 @@ class Chef :long => "--force", :description => "Force download deprecated version" + option :supermarket_site, + :short => "-m SUPERMARKET_SITE", + :long => "--supermarket-site SUPERMARKET_SITE", + :description => "Supermarket Site", + :default => "https://supermarket.chef.io", + :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket } + def run if current_cookbook_deprecated? message = "DEPRECATION: This cookbook has been deprecated. " @@ -59,7 +66,7 @@ class Chef private def cookbooks_api_url - "https://supermarket.chef.io/api/v1/cookbooks" + "#{config[:supermarket_site]}/api/v1/cookbooks" end def current_cookbook_data diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb index 45f3061d87..43d015dcc4 100644 --- a/lib/chef/knife/cookbook_site_install.rb +++ b/lib/chef/knife/cookbook_site_install.rb @@ -19,6 +19,7 @@ require "chef/knife" require "chef/exceptions" require "shellwords" +require "mixlib/archive" class Chef class Knife @@ -59,6 +60,13 @@ class Chef :boolean => true, :default => false + option :supermarket_site, + :short => "-m SUPERMARKET_SITE", + :long => "--supermarket-site SUPERMARKET_SITE", + :description => "Supermarket Site", + :default => "https://supermarket.chef.io", + :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket } + attr_reader :cookbook_name attr_reader :vendor_path @@ -134,6 +142,7 @@ class Chef def download_cookbook_to(download_path) downloader = Chef::Knife::CookbookSiteDownload.new downloader.config[:file] = download_path + downloader.config[:supermarket_site] = config[:supermarket_site] downloader.name_args = name_args downloader.run downloader @@ -141,17 +150,7 @@ class Chef def extract_cookbook(upstream_file, version) ui.info("Uncompressing #{@cookbook_name} version #{version}.") - extract_command = "tar zxvf \"#{convert_path upstream_file}\"" - if Chef::Platform.windows? - tar_version = shell_out("tar --version").stdout.tr("\n", " ") - if tar_version =~ /GNU tar/ - Chef::Log.debug("GNU tar detected, adding --force-local") - extract_command << " --force-local" - else - Chef::Log.debug("non-GNU tar detected, not adding --force-local") - end - end - shell_out!(extract_command, :cwd => @install_path) + Mixlib::Archive.new(convert_path(upstream_file)).extract(@install_path, perms: false) end def clear_existing_files(cookbook_path) diff --git a/lib/chef/knife/cookbook_site_list.rb b/lib/chef/knife/cookbook_site_list.rb index abe48bf340..3bdef8abe5 100644 --- a/lib/chef/knife/cookbook_site_list.rb +++ b/lib/chef/knife/cookbook_site_list.rb @@ -30,6 +30,13 @@ class Chef :long => "--with-uri", :description => "Show corresponding URIs" + option :supermarket_site, + :short => "-m SUPERMARKET_SITE", + :long => "--supermarket-site SUPERMARKET_SITE", + :description => "Supermarket Site", + :default => "https://supermarket.chef.io", + :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket } + def run if config[:with_uri] cookbooks = Hash.new @@ -41,7 +48,7 @@ class Chef end def get_cookbook_list(items = 10, start = 0, cookbook_collection = {}) - cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}" + cookbooks_url = "#{config[:supermarket_site]}/api/v1/cookbooks?items=#{items}&start=#{start}" cr = noauth_rest.get(cookbooks_url) cr["items"].each do |cookbook| cookbook_collection[cookbook["cookbook_name"]] = cookbook diff --git a/lib/chef/knife/cookbook_site_search.rb b/lib/chef/knife/cookbook_site_search.rb index ba4b873efc..d401844217 100644 --- a/lib/chef/knife/cookbook_site_search.rb +++ b/lib/chef/knife/cookbook_site_search.rb @@ -24,12 +24,19 @@ class Chef banner "knife cookbook site search QUERY (options)" category "cookbook site" + option :supermarket_site, + :short => "-m SUPERMARKET_SITE", + :long => "--supermarket-site SUPERMARKET_SITE", + :description => "Supermarket Site", + :default => "https://supermarket.chef.io", + :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket } + def run output(search_cookbook(name_args[0])) end def search_cookbook(query, items = 10, start = 0, cookbook_collection = {}) - cookbooks_url = "https://supermarket.chef.io/api/v1/search?q=#{query}&items=#{items}&start=#{start}" + cookbooks_url = "#{config[:supermarket_site]}/api/v1/search?q=#{query}&items=#{items}&start=#{start}" cr = noauth_rest.get(cookbooks_url) cr["items"].each do |cookbook| cookbook_collection[cookbook["cookbook_name"]] = cookbook diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb index 6f37568f5f..d55d6c123a 100644 --- a/lib/chef/knife/cookbook_site_share.rb +++ b/lib/chef/knife/cookbook_site_share.rb @@ -50,6 +50,13 @@ class Chef :default => false, :description => "Don't take action, only print what files will be uploaded to Supermarket." + option :supermarket_site, + :short => "-m SUPERMARKET_SITE", + :long => "--supermarket-site SUPERMARKET_SITE", + :description => "Supermarket Site", + :default => "https://supermarket.chef.io", + :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket } + def run config[:cookbook_path] ||= Chef::Config[:cookbook_path] @@ -106,23 +113,17 @@ class Chef end def get_category(cookbook_name) - begin - data = noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}") - if !data["category"] && data["error_code"] - ui.fatal("Received an error from Supermarket: #{data["error_code"]}. On the first time you upload it, you are required to specify the category you want to share this cookbook to.") - exit(1) - else - data["category"] - end - rescue => e - ui.fatal("Unable to reach Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.") - Chef::Log.debug("\n#{e.backtrace.join("\n")}") - exit(1) - end + data = noauth_rest.get("#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}") + data["category"] + rescue => e + return "Other" if e.kind_of?(Net::HTTPServerException) && e.response.code == "404" + ui.fatal("Unable to reach Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.") + Chef::Log.debug("\n#{e.backtrace.join("\n")}") + exit(1) end def do_upload(cookbook_filename, cookbook_category, user_id, user_secret_filename) - uri = "https://supermarket.chef.io/api/v1/cookbooks" + uri = "#{config[:supermarket_site]}/api/v1/cookbooks" category_string = Chef::JSONCompat.to_json({ "category" => cookbook_category }) diff --git a/lib/chef/knife/cookbook_site_show.rb b/lib/chef/knife/cookbook_site_show.rb index c0280cb318..ce153ca5a1 100644 --- a/lib/chef/knife/cookbook_site_show.rb +++ b/lib/chef/knife/cookbook_site_show.rb @@ -24,21 +24,32 @@ class Chef banner "knife cookbook site show COOKBOOK [VERSION] (options)" category "cookbook site" + option :supermarket_site, + :short => "-m SUPERMARKET_SITE", + :long => "--supermarket-site SUPERMARKET_SITE", + :description => "Supermarket Site", + :default => "https://supermarket.chef.io", + :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket } + def run output(format_for_display(get_cookbook_data)) end + def supermarket_uri + "#{config[:supermarket_site]}/api/v1" + end + def get_cookbook_data case @name_args.length when 1 - noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}") + noauth_rest.get("#{supermarket_uri}/cookbooks/#{@name_args[0]}") when 2 - noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}/versions/#{name_args[1].tr('.', '_')}") + noauth_rest.get("#{supermarket_uri}/cookbooks/#{@name_args[0]}/versions/#{name_args[1].tr('.', '_')}") end end def get_cookbook_list(items = 10, start = 0, cookbook_collection = {}) - cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}" + cookbooks_url = "#{supermarket_uri}/cookbooks?items=#{items}&start=#{start}" cr = noauth_rest.get(cookbooks_url) cr["items"].each do |cookbook| cookbook_collection[cookbook["cookbook_name"]] = cookbook diff --git a/lib/chef/knife/cookbook_site_unshare.rb b/lib/chef/knife/cookbook_site_unshare.rb index 310f6ac41d..bdabff0b94 100644 --- a/lib/chef/knife/cookbook_site_unshare.rb +++ b/lib/chef/knife/cookbook_site_unshare.rb @@ -30,6 +30,13 @@ class Chef banner "knife cookbook site unshare COOKBOOK" category "cookbook site" + option :supermarket_site, + :short => "-m SUPERMARKET_SITE", + :long => "--supermarket-site SUPERMARKET_SITE", + :description => "Supermarket Site", + :default => "https://supermarket.chef.io", + :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket } + def run @cookbook_name = @name_args[0] if @cookbook_name.nil? @@ -41,7 +48,7 @@ class Chef confirm "Do you really want to unshare all versions of the cookbook #{@cookbook_name}" begin - rest.delete "https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}" + rest.delete "#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}" rescue Net::HTTPServerException => e raise e unless e.message =~ /Forbidden/ ui.error "Forbidden: You must be the maintainer of #{@cookbook_name} to unshare it." diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb index 48d2cb9e77..b2670f196b 100644 --- a/lib/chef/knife/core/bootstrap_context.rb +++ b/lib/chef/knife/core/bootstrap_context.rb @@ -54,7 +54,7 @@ class Chef end def client_d - @cliend_d ||= client_d_content + @client_d ||= client_d_content end def encrypted_data_bag_secret @@ -114,6 +114,16 @@ validation_client_name "#{@chef_config[:validation_client_name]}" client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n} end + if knife_config[:bootstrap_proxy_user] + client_rb << %Q{http_proxy_user "#{knife_config[:bootstrap_proxy_user]}"\n} + client_rb << %Q{https_proxy_user "#{knife_config[:bootstrap_proxy_user]}"\n} + end + + if knife_config[:bootstrap_proxy_pass] + client_rb << %Q{http_proxy_pass "#{knife_config[:bootstrap_proxy_pass]}"\n} + client_rb << %Q{https_proxy_pass "#{knife_config[:bootstrap_proxy_pass]}"\n} + end + if knife_config[:bootstrap_no_proxy] client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n} end diff --git a/lib/chef/knife/supermarket_download.rb b/lib/chef/knife/supermarket_download.rb new file mode 100644 index 0000000000..5657558591 --- /dev/null +++ b/lib/chef/knife/supermarket_download.rb @@ -0,0 +1,33 @@ +# +# Author:: Christopher Webber (<cwebber@chef.io>) +# Copyright:: Copyright (c) 2014 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 "chef/knife" +require "chef/knife/cookbook_site_download" + +class Chef + class Knife + class SupermarketDownload < Knife::CookbookSiteDownload + # Handle the subclassing (knife doesn't do this :() + dependency_loaders.concat(superclass.dependency_loaders) + options.merge!(superclass.options) + + banner "knife supermarket download COOKBOOK [VERSION] (options)" + category "supermarket" + end + end +end diff --git a/lib/chef/knife/supermarket_install.rb b/lib/chef/knife/supermarket_install.rb new file mode 100644 index 0000000000..7642e68181 --- /dev/null +++ b/lib/chef/knife/supermarket_install.rb @@ -0,0 +1,33 @@ +# +# Author:: Christopher Webber (<cwebber@chef.io>) +# Copyright:: Copyright (c) 2014 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 "chef/knife" +require "chef/knife/cookbook_site_install" + +class Chef + class Knife + class SupermarketInstall < Knife::CookbookSiteInstall + # Handle the subclassing (knife doesn't do this :() + dependency_loaders.concat(superclass.dependency_loaders) + options.merge!(superclass.options) + + banner "knife supermarket install COOKBOOK [VERSION] (options)" + category "supermarket" + end + end +end diff --git a/lib/chef/knife/supermarket_list.rb b/lib/chef/knife/supermarket_list.rb new file mode 100644 index 0000000000..f2bc98bd0e --- /dev/null +++ b/lib/chef/knife/supermarket_list.rb @@ -0,0 +1,33 @@ +# +# Author:: Christopher Webber (<cwebber@chef.io>) +# Copyright:: Copyright (c) 2014 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 "chef/knife" +require "chef/knife/cookbook_site_list" + +class Chef + class Knife + class SupermarketList < Knife::CookbookSiteList + # Handle the subclassing (knife doesn't do this :() + dependency_loaders.concat(superclass.dependency_loaders) + options.merge!(superclass.options) + + banner "knife supermarket list (options)" + category "supermarket" + end + end +end diff --git a/lib/chef/knife/supermarket_search.rb b/lib/chef/knife/supermarket_search.rb new file mode 100644 index 0000000000..3206b0cb80 --- /dev/null +++ b/lib/chef/knife/supermarket_search.rb @@ -0,0 +1,33 @@ +# +# Author:: Christopher Webber (<cwebber@chef.io>) +# Copyright:: Copyright (c) 2014 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 "chef/knife" +require "chef/knife/cookbook_site_search" + +class Chef + class Knife + class SupermarketSearch < Knife::CookbookSiteSearch + # Handle the subclassing (knife doesn't do this :() + dependency_loaders.concat(superclass.dependency_loaders) + options.merge!(superclass.options) + + banner "knife supermarket search QUERY (options)" + category "supermarket" + end + end +end diff --git a/lib/chef/knife/supermarket_share.rb b/lib/chef/knife/supermarket_share.rb new file mode 100644 index 0000000000..3109b9e794 --- /dev/null +++ b/lib/chef/knife/supermarket_share.rb @@ -0,0 +1,33 @@ +# +# Author:: Christopher Webber (<cwebber@chef.io>) +# Copyright:: Copyright (c) 2014 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 "chef/knife" +require "chef/knife/cookbook_site_share" + +class Chef + class Knife + class SupermarketShare < Knife::CookbookSiteShare + # Handle the subclassing (knife doesn't do this :() + dependency_loaders.concat(superclass.dependency_loaders) + options.merge!(superclass.options) + + banner "knife supermarket share COOKBOOK [CATEGORY] (options)" + category "supermarket" + end + end +end diff --git a/lib/chef/knife/supermarket_show.rb b/lib/chef/knife/supermarket_show.rb new file mode 100644 index 0000000000..2ad122143f --- /dev/null +++ b/lib/chef/knife/supermarket_show.rb @@ -0,0 +1,33 @@ +# +# Author:: Christopher Webber (<cwebber@chef.io>) +# Copyright:: Copyright (c) 2014 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 "chef/knife" +require "chef/knife/cookbook_site_show" + +class Chef + class Knife + class SupermarketShow < Knife::CookbookSiteShow + # Handle the subclassing (knife doesn't do this :() + dependency_loaders.concat(superclass.dependency_loaders) + options.merge!(superclass.options) + + banner "knife supermarket show COOKBOOK [VERSION] (options)" + category "supermarket" + end + end +end diff --git a/lib/chef/knife/supermarket_unshare.rb b/lib/chef/knife/supermarket_unshare.rb new file mode 100644 index 0000000000..fd48e172ce --- /dev/null +++ b/lib/chef/knife/supermarket_unshare.rb @@ -0,0 +1,33 @@ +# +# Author:: Christopher Webber (<cwebber@chef.io>) +# Copyright:: Copyright (c) 2014 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 "chef/knife" +require "chef/knife/cookbook_site_unshare" + +class Chef + class Knife + class SupermarketUnshare < Knife::CookbookSiteUnshare + # Handle the subclassing (knife doesn't do this :() + dependency_loaders.concat(superclass.dependency_loaders) + options.merge!(superclass.options) + + banner "knife supermarket unshare COOKBOOK (options)" + category "supermarket" + end + end +end diff --git a/lib/chef/node.rb b/lib/chef/node.rb index 8d77becbf0..54faab6d3e 100644 --- a/lib/chef/node.rb +++ b/lib/chef/node.rb @@ -43,6 +43,8 @@ class Chef def_delegators :attributes, :keys, :each_key, :each_value, :key?, :has_key? def_delegators :attributes, :rm, :rm_default, :rm_normal, :rm_override def_delegators :attributes, :default!, :normal!, :override!, :force_default!, :force_override! + def_delegators :attributes, :default_unless, :normal_unless, :override_unless, :set_unless + def_delegators :attributes, :read, :read!, :write, :write!, :unlink, :unlink! attr_accessor :recipe_list, :run_state, :override_runlist @@ -196,35 +198,18 @@ class Chef # might be missing def normal attributes.top_level_breadcrumb = nil - attributes.set_unless_value_present = false attributes.normal end - alias_method :set, :normal - - # Set a normal attribute of this node, auto-vivifying any mashes that are - # missing, but if the final value already exists, don't set it - def normal_unless - attributes.top_level_breadcrumb = nil - attributes.set_unless_value_present = true - attributes.normal + def set + Chef.log_deprecation("node.set is deprecated and will be removed in Chef 14, please use node.default/node.override (or node.normal only if you really need persistence)") + normal end - alias_method :set_unless, :normal_unless - # Set a default of this node, but auto-vivify any Mashes that might # be missing def default attributes.top_level_breadcrumb = nil - attributes.set_unless_value_present = false - attributes.default - end - - # Set a default attribute of this node, auto-vivifying any mashes that are - # missing, but if the final value already exists, don't set it - def default_unless - attributes.top_level_breadcrumb = nil - attributes.set_unless_value_present = true attributes.default end @@ -232,15 +217,6 @@ class Chef # might be missing def override attributes.top_level_breadcrumb = nil - attributes.set_unless_value_present = false - attributes.override - end - - # Set an override attribute of this node, auto-vivifying any mashes that - # are missing, but if the final value already exists, don't set it - def override_unless - attributes.top_level_breadcrumb = nil - attributes.set_unless_value_present = true attributes.override end @@ -262,7 +238,6 @@ class Chef def automatic_attrs attributes.top_level_breadcrumb = nil - attributes.set_unless_value_present = false attributes.automatic end @@ -290,8 +265,14 @@ class Chef end # Only works for attribute fetches, setting is no longer supported - def method_missing(symbol, *args) - attributes.send(symbol, *args) + # XXX: this should be deprecated + def method_missing(method, *args, &block) + attributes.public_send(method, *args, &block) + end + + # Fix respond_to + method so that it works with method_missing delegation + def respond_to_missing?(method, include_private = false) + attributes.respond_to?(method, false) end # Returns true if this Node expects a given recipe, false if not. diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb index ab97cf99bf..f5fe89251d 100644 --- a/lib/chef/node/attribute.rb +++ b/lib/chef/node/attribute.rb @@ -19,6 +19,7 @@ require "chef/node/immutable_collections" require "chef/node/attribute_collections" +require "chef/decorator/unchain" require "chef/mixin/deep_merge" require "chef/log" @@ -132,6 +133,7 @@ class Chef :take, :take_while, :to_a, + :to_h, :to_hash, :to_set, :value?, @@ -187,8 +189,6 @@ class Chef attr_accessor :deep_merge_cache def initialize(normal, default, override, automatic) - @set_unless_present = false - @default = VividMash.new(self, default) @env_default = VividMash.new(self, {}) @role_default = VividMash.new(self, {}) @@ -214,15 +214,13 @@ class Chef # attribute you're interested in. For example, to debug where the value # of `node[:network][:default_interface]` is coming from, use: # debug_value(:network, :default_interface). - # The return value is an Array of Arrays. The first element is - # `["set_unless_enabled?", Boolean]`, which describes whether the - # attribute collection is in "set_unless" mode. The rest of the Arrays + # The return value is an Array of Arrays. The Arrays # are pairs of `["precedence_level", value]`, where precedence level is # the component, such as role default, normal, etc. and value is the # attribute value set at that precedence level. If there is no value at # that precedence level, +value+ will be the symbol +:not_present+. def debug_value(*args) - components = COMPONENTS.map do |component| + COMPONENTS.map do |component| ivar = instance_variable_get(component) value = args.inject(ivar) do |so_far, key| if so_far == :not_present @@ -235,12 +233,6 @@ class Chef end [component.to_s.sub(/^@/, ""), value] end - [["set_unless_enabled?", @set_unless_present]] + components - end - - # Enables or disables `||=`-like attribute setting. See, e.g., Node#set_unless - def set_unless_value_present=(setting) - @set_unless_present = setting end # Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate @@ -321,94 +313,134 @@ class Chef # clears attributes from all precedence levels def rm(*args) - reset(args[0]) - # just easier to compute our retval, rather than collect+merge sub-retvals - ret = args.inject(merged_attributes) do |attr, arg| - if attr.nil? || !attr.respond_to?(:[]) - nil - else - begin - attr[arg] - rescue TypeError - raise TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)" - end - end + with_deep_merged_return_value(self, *args) do + rm_default(*args) + rm_normal(*args) + rm_override(*args) end - rm_default(*args) - rm_normal(*args) - rm_override(*args) - ret end - # does <level>['foo']['bar'].delete('baz') - def remove_from_precedence_level(level, *args, key) - multimash = level.element(*args) - multimash.nil? ? nil : multimash.delete(key) - end - - private :remove_from_precedence_level - # clears attributes from all default precedence levels # - # equivalent to: force_default!['foo']['bar'].delete('baz') + # similar to: force_default!['foo']['bar'].delete('baz') + # - does not autovivify + # - does not trainwreck if interior keys do not exist def rm_default(*args) - reset(args[0]) - remove_from_precedence_level(force_default!(autovivify: false), *args) + with_deep_merged_return_value(combined_default, *args) do + default.unlink(*args) + role_default.unlink(*args) + env_default.unlink(*args) + force_default.unlink(*args) + end end # clears attributes from normal precedence # # equivalent to: normal!['foo']['bar'].delete('baz') + # - does not autovivify + # - does not trainwreck if interior keys do not exist def rm_normal(*args) - reset(args[0]) - remove_from_precedence_level(normal!(autovivify: false), *args) + normal.unlink(*args) end # clears attributes from all override precedence levels # # equivalent to: force_override!['foo']['bar'].delete('baz') + # - does not autovivify + # - does not trainwreck if interior keys do not exist def rm_override(*args) - reset(args[0]) - remove_from_precedence_level(force_override!(autovivify: false), *args) + with_deep_merged_return_value(combined_override, *args) do + override.unlink(*args) + role_override.unlink(*args) + env_override.unlink(*args) + force_override.unlink(*args) + end + end + + def with_deep_merged_return_value(obj, *path, last) + hash = obj.read(*path) + return nil unless hash.is_a?(Hash) + ret = hash[last] + yield + ret end + private :with_deep_merged_return_value + # # Replacing attributes without merging # # sets default attributes without merging - def default!(opts = {}) - # FIXME: do not flush whole cache - reset - MultiMash.new(self, @default, [], opts) + # + # - this API autovivifies (and cannot trainwreck) + def default!(*args) + return Decorator::Unchain.new(self, :default!) unless args.length > 0 + write(:default, *args) end # sets normal attributes without merging - def normal!(opts = {}) - # FIXME: do not flush whole cache - reset - MultiMash.new(self, @normal, [], opts) + # + # - this API autovivifies (and cannot trainwreck) + def normal!(*args) + return Decorator::Unchain.new(self, :normal!) unless args.length > 0 + write(:normal, *args) end # sets override attributes without merging - def override!(opts = {}) - # FIXME: do not flush whole cache - reset - MultiMash.new(self, @override, [], opts) + # + # - this API autovivifies (and cannot trainwreck) + def override!(*args) + return Decorator::Unchain.new(self, :override!) unless args.length > 0 + write(:override, *args) end # clears from all default precedence levels and then sets force_default - def force_default!(opts = {}) - # FIXME: do not flush whole cache - reset - MultiMash.new(self, @force_default, [@default, @env_default, @role_default], opts) + # + # - this API autovivifies (and cannot trainwreck) + def force_default!(*args) + return Decorator::Unchain.new(self, :force_default!) unless args.length > 0 + value = args.pop + rm_default(*args) + write(:force_default, *args, value) end # clears from all override precedence levels and then sets force_override - def force_override!(opts = {}) - # FIXME: do not flush whole cache - reset - MultiMash.new(self, @force_override, [@override, @env_override, @role_override], opts) + def force_override!(*args) + return Decorator::Unchain.new(self, :force_override!) unless args.length > 0 + value = args.pop + rm_override(*args) + 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 # @@ -420,9 +452,9 @@ class Chef # def merged_attributes(*path) - # immutablize( + # immutablize( merge_all(path) - # ) + # ) end def combined_override(*path) @@ -433,6 +465,27 @@ class Chef immutablize(merge_defaults(path)) end + def normal_unless(*args) + return Decorator::Unchain.new(self, :normal_unless) unless args.length > 0 + write(:normal, *args) if read(*args[0...-1]).nil? + end + + def default_unless(*args) + return Decorator::Unchain.new(self, :default_unless) unless args.length > 0 + write(:default, *args) if read(*args[0...-1]).nil? + end + + def override_unless(*args) + return Decorator::Unchain.new(self, :override_unless) unless args.length > 0 + write(:override, *args) if read(*args[0...-1]).nil? + end + + def set_unless(*args) + Chef.log_deprecation("node.set_unless is deprecated and will be removed in Chef 14, please use node.default_unless/node.override_unless (or node.normal_unless if you really need persistence)") + return Decorator::Unchain.new(self, :default_unless) unless args.length > 0 + 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 @@ -461,13 +514,17 @@ class Chef alias :each_attribute :each def method_missing(symbol, *args) - if args.empty? + if symbol == :to_ary + merged_attributes.send(symbol, *args) + elsif args.empty? + Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])} if key?(symbol) self[symbol] else raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'" end elsif symbol.to_s =~ /=$/ + Chef.log_deprecation %q{method setting of node attributes (node.foo="bar") is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]="bar")} key_to_set = symbol.to_s[/^(.+)=$/, 1] self[key_to_set] = (args.length == 1 ? args[0] : args) else @@ -485,10 +542,6 @@ class Chef }.join(", ") << ">" end - def set_unless? - @set_unless_present - end - private # Helper method for merge_all/merge_defaults/merge_overrides. diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb index 68f3a69756..b739ea8490 100644 --- a/lib/chef/node/attribute_collections.rb +++ b/lib/chef/node/attribute_collections.rb @@ -16,15 +16,15 @@ # limitations under the License. # +require "chef/node/common_api" + class Chef class Node - # == AttrArray # AttrArray is identical to Array, except that it keeps a reference to the # "root" (Chef::Node::Attribute) object, and will trigger a cache # invalidation on that object when mutated. class AttrArray < Array - MUTATOR_METHODS = [ :<<, :[]=, @@ -62,8 +62,9 @@ class Chef # Node::Attribute object. MUTATOR_METHODS.each do |mutator| define_method(mutator) do |*args, &block| + ret = super(*args, &block) root.reset_cache(root.top_level_breadcrumb) - super(*args, &block) + ret end end @@ -96,14 +97,12 @@ class Chef # in the creation of a new VividMash for that key. (This only works when # using the element reference method, `[]` -- other methods, such as # #fetch, work as normal). - # * It supports a set_unless flag (via the root Attribute object) which - # allows `||=` style behavior (`||=` does not work with - # auto-vivification). This is only implemented for #[]=; methods such as - # #store 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 # also invalidates the cached merged_attributes on the root Attribute # object. @@ -148,12 +147,9 @@ class Chef def []=(key, value) root.top_level_breadcrumb ||= key - if set_unless? && key?(key) && !self[key].nil? - self[key] - else - root.reset_cache(root.top_level_breadcrumb) - super - end + ret = super + root.reset_cache(root.top_level_breadcrumb) + ret end alias :attribute? :has_key? @@ -176,10 +172,6 @@ class Chef end end - def set_unless? - @root.set_unless? - end - def convert_key(key) super end @@ -206,118 +198,5 @@ class Chef end end - - # == MultiMash - # This is a Hash-like object that contains multiple VividMashes in it. Its - # purpose is so that the user can descend into the mash and delete a subtree - # from all of the Mash objects (used to delete all values in a subtree from - # default, force_default, role_default and env_default at the same time). The - # assignment operator strictly does assignment (does no merging) and works - # by deleting the subtree and then assigning to the last mash which passed in - # the initializer. - # - # A lot of the complexity of this class comes from the fact that at any key - # value some or all of the mashes may walk off their ends and become nil or - # true or something. The schema may change so that one precidence leve may - # be 'true' object and another may be a VividMash. It is also possible that - # one or many of them may transition from VividMashes to Hashes or Arrays. - # - # It also supports the case where you may be deleting a key using node.rm - # in which case if intermediate keys all walk off into nil then you don't want - # to be autovivifying keys as you go. On the other hand you may be using - # node.force_default! in which case you'll wind up with a []= operator at the - # end and you want autovivification, so we conditionally have to support either - # operation. - # - # @todo: can we have an autovivify class that decorates a class that doesn't - # autovivify or something so that the code is less awful? - # - class MultiMash - attr_reader :root - attr_reader :mashes - attr_reader :opts - attr_reader :primary_mash - - # Initialize with an array of mashes. For the delete return value to work - # properly the mashes must come from the same attribute level (i.e. all - # override or all default, but not a mix of both). - def initialize(root, primary_mash, mashes, opts = {}) - @root = root - @primary_mash = primary_mash - @mashes = mashes - @opts = opts - @opts[:autovivify] = true if @opts[:autovivify].nil? - end - - def [](key) - # handle the secondary mashes - new_mashes = [] - mashes.each do |mash| - new_mash = safe_evalute_key(mash, key) - # secondary mashes never autovivify so once they fall into nil, we just stop tracking them - new_mashes.push(new_mash) unless new_mash.nil? - end - - new_primary_mash = safe_evalute_key(primary_mash, key) - - if new_primary_mash.nil? && @opts[:autovivify] - primary_mash[key] = VividMash.new(root) - new_primary_mash = primary_mash[key] - end - - MultiMash.new(root, new_primary_mash, new_mashes, opts) - end - - def []=(key, value) - if primary_mash.nil? - # This theoretically should never happen since node#force_default! setter methods will autovivify and - # node#rm methods do not end in #[]= operators. - raise TypeError, "No autovivification was specified initially on a method chain ending in assignment" - end - ret = delete(key) - primary_mash[key] = value - ret - end - - # mash.element('foo', 'bar') is the same as mash['foo']['bar'] - def element(key = nil, *subkeys) - return self if key.nil? - submash = self[key] - subkeys.empty? ? submash : submash.element(*subkeys) - end - - def delete(key) - # the return value is a deep merge which is correct semantics when - # merging between attributes on the same level (this would be incorrect - # if passed both override and default attributes which would need hash_only - # merging). - ret = mashes.inject(Mash.new) do |merged, mash| - Chef::Mixin::DeepMerge.merge(merged, mash) - end - ret = Chef::Mixin::DeepMerge.merge(ret, primary_mash) - mashes.each do |mash| - mash.delete(key) if mash.respond_to?(:delete) - end - primary_mash.delete(key) if primary_mash.respond_to?(:delete) - ret[key] - end - - private - - def safe_evalute_key(mash, key) - if mash.respond_to?(:[]) - if mash.respond_to?(:has_key?) - if mash.has_key?(key) - return mash[key] if mash[key].respond_to?(:[]) - end - elsif !mash[key].nil? - return mash[key] if mash[key].respond_to?(:[]) - end - end - return nil - end - - end - end end diff --git a/lib/chef/node/common_api.rb b/lib/chef/node/common_api.rb new file mode 100644 index 0000000000..ce2c6b6878 --- /dev/null +++ b/lib/chef/node/common_api.rb @@ -0,0 +1,129 @@ +#-- +# 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 + # shared API between VividMash and ImmutableMash, writer code can be + # 'shared' to keep it logically in this file by adding them to the + # block list in ImmutableMash. + module CommonAPI + # method-style access to attributes + + def valid_container?(obj, key) + return obj.is_a?(Hash) || (obj.is_a?(Array) && key.is_a?(Fixnum)) + end + + private :valid_container? + + # - 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 + chain = args.inject(self) do |memo, key| + if !valid_container?(memo, key) + prev_memo[prev_key] = {} + memo = prev_memo[prev_key] + end + prev_memo = memo + prev_key = key + memo[key] + end + if !valid_container?(chain, last) + prev_memo[prev_key] = {} + chain = prev_memo[prev_key] + end + chain[last] = value + end + + # this autovivifies, but can throw NoSuchAttribute when trying to access #[] on + # 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| + raise Chef::Exceptions::AttributeTypeMismatch unless valid_container?(memo, key) + memo[key] + end + raise Chef::Exceptions::AttributeTypeMismatch unless valid_container?(obj, last) + obj[last] = value + end + + # FIXME:(?) does anyone need a non-autovivifying writer for attributes that throws exceptions? + + # 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) + if memo.key?(key) + memo[key] + else + return false + end + elsif memo.is_a?(Array) + if memo.length > key + memo[key] + else + return false + end + end + end + return true + end + + # this is a safe non-autovivifying reader that returns nil if the attribute does not exist + def read(*path) + begin + read!(*path) + rescue Chef::Exceptions::NoSuchAttribute + nil + end + end + + # 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 + end + + # 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 + + def unlink!(*path) + raise Chef::Exceptions::NoSuchAttribute unless exist?(*path) + unlink(*path) + end + + end + end +end diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb index b5fd86fa72..d4623ace2a 100644 --- a/lib/chef/node/immutable_collections.rb +++ b/lib/chef/node/immutable_collections.rb @@ -1,3 +1,21 @@ +#-- +# Copyright:: Copyright 2012-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 "chef/node/common_api" class Chef class Node @@ -124,6 +142,7 @@ class Chef class ImmutableMash < Mash include Immutablize + include CommonAPI alias :internal_set :[]= private :internal_set @@ -144,6 +163,10 @@ class Chef :replace, :select!, :shift, + :write, + :write!, + :unlink, + :unlink!, ] def initialize(mash_data) @@ -167,13 +190,15 @@ class Chef end def method_missing(symbol, *args) - if args.empty? + if symbol == :to_ary + super + elsif args.empty? if key?(symbol) self[symbol] else raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'" end - # This will raise a ImmutableAttributeModification error: + # This will raise a ImmutableAttributeModification error: elsif symbol.to_s =~ /=$/ key_to_set = symbol.to_s[/^(.+)=$/, 1] self[key_to_set] = (args.length == 1 ? args[0] : args) diff --git a/lib/chef/property.rb b/lib/chef/property.rb index 45ab4dd522..0589cb4c54 100644 --- a/lib/chef/property.rb +++ b/lib/chef/property.rb @@ -531,8 +531,6 @@ class Chef end end - protected - # # The options this Property will use for get/set behavior and validation. # @@ -583,6 +581,7 @@ class Chef (options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false)) end + # @api private def get_value(resource) if instance_variable_name resource.instance_variable_get(instance_variable_name) @@ -591,6 +590,7 @@ class Chef end end + # @api private def set_value(resource, value) if instance_variable_name resource.instance_variable_set(instance_variable_name, value) @@ -599,6 +599,7 @@ class Chef end end + # @api private def value_is_set?(resource) if instance_variable_name resource.instance_variable_defined?(instance_variable_name) @@ -607,6 +608,7 @@ class Chef end end + # @api private def reset_value(resource) if instance_variable_name if value_is_set?(resource) @@ -617,6 +619,8 @@ class Chef end end + private + def exec_in_resource(resource, proc, *args) if resource if proc.arity > args.size diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb index 03b546c09d..7cfddba0cb 100644 --- a/lib/chef/provider.rb +++ b/lib/chef/provider.rb @@ -423,9 +423,9 @@ class Chef module DeprecatedLWRPClass def const_missing(class_name) - if deprecated_constants[class_name.to_sym] + if Chef::Provider.deprecated_constants[class_name.to_sym] Chef.log_deprecation("Using an LWRP provider by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed. Use Chef::ProviderResolver.new(node, resource, action) instead.") - deprecated_constants[class_name.to_sym] + Chef::Provider.deprecated_constants[class_name.to_sym] else raise NameError, "uninitialized constant Chef::Provider::#{class_name}" end @@ -438,13 +438,12 @@ class Chef if Chef::Provider.const_defined?(class_name, false) Chef::Log.warn "Chef::Provider::#{class_name} already exists! Cannot create deprecation class for #{provider_class}" else - deprecated_constants[class_name.to_sym] = provider_class + Chef::Provider.deprecated_constants[class_name.to_sym] = provider_class end end - private - def deprecated_constants + raise "Deprecated constants should be called only on Chef::Provider" unless self == Chef::Provider @deprecated_constants ||= {} end end diff --git a/lib/chef/provider/batch.rb b/lib/chef/provider/batch.rb index bb294afd3f..0d857aaa79 100644 --- a/lib/chef/provider/batch.rb +++ b/lib/chef/provider/batch.rb @@ -29,7 +29,7 @@ class Chef end def command - basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory + basepath = is_forced_32bit ? wow64_directory : run_context.node["kernel"]["os_info"]["system_directory"] interpreter_path = Chef::Util::PathHelper.join(basepath, interpreter) diff --git a/lib/chef/provider/cron.rb b/lib/chef/provider/cron.rb index 36b67ab6a5..c7487cf42f 100644 --- a/lib/chef/provider/cron.rb +++ b/lib/chef/provider/cron.rb @@ -237,7 +237,7 @@ class Chef newcron = "" newcron << "# Chef Name: #{new_resource.name}\n" [ :mailto, :path, :shell, :home ].each do |v| - newcron << "#{v.to_s.upcase}=#{@new_resource.send(v)}\n" if @new_resource.send(v) + newcron << "#{v.to_s.upcase}=\"#{@new_resource.send(v)}\"\n" if @new_resource.send(v) end @new_resource.environment.each do |name, value| newcron << "#{name}=#{value}\n" diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb index 7f85085eeb..bb0762ceb7 100644 --- a/lib/chef/provider/file.rb +++ b/lib/chef/provider/file.rb @@ -154,6 +154,7 @@ class Chef do_contents_changes do_acl_changes do_selinux + do_resolv_conf_fixup load_resource_attributes_from_file(@new_resource) end @@ -445,6 +446,13 @@ class Chef end end + def do_resolv_conf_fixup + # reload /etc/resolv.conf after we edit it -- only on linux -- and see lib/chef/application.rb + if new_resource.path == "/etc/resolv.conf" && RbConfig::CONFIG["host_os"] =~ /linux/ + Resolv::DefaultResolver.replace_resolvers [Resolv::DNS.new("/etc/resolv.conf")] + end + end + def do_acl_changes if access_controls.requires_changes? converge_by(access_controls.describe_changes) do diff --git a/lib/chef/provider/package/aix.rb b/lib/chef/provider/package/aix.rb index a1709c4af7..728f181055 100644 --- a/lib/chef/provider/package/aix.rb +++ b/lib/chef/provider/package/aix.rb @@ -55,7 +55,11 @@ class Chef ret = shell_out_with_timeout("installp -L -d #{@new_resource.source}") ret.stdout.each_line do |line| case line - when /#{@new_resource.package_name}:/ + when /:#{@new_resource.package_name}:/ + fields = line.split(":") + @new_resource.version(fields[2]) + when /^#{@new_resource.package_name}:/ + Chef::Log.warn("You are installing a bff package by product name. For idempotent installs, please install individual filesets") fields = line.split(":") @new_resource.version(fields[2]) end diff --git a/lib/chef/provider/package/openbsd.rb b/lib/chef/provider/package/openbsd.rb index 2120b9aa48..8043c01693 100644 --- a/lib/chef/provider/package/openbsd.rb +++ b/lib/chef/provider/package/openbsd.rb @@ -127,7 +127,7 @@ class Chef end def pkg_path - ENV["PKG_PATH"] || "http://ftp.OpenBSD.org/pub/#{node.kernel.name}/#{node.kernel.release}/packages/#{node.kernel.machine}/" + ENV["PKG_PATH"] || "http://ftp.OpenBSD.org/pub/#{node["kernel"]["name"]}/#{node["kernel"]["release"]}/packages/#{node["kernel"]["machine"]}/" end end diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb index eb5a87099f..0aeec951b1 100644 --- a/lib/chef/provider/package/rubygems.rb +++ b/lib/chef/provider/package/rubygems.rb @@ -431,17 +431,23 @@ class Chef end def current_version - #raise 'todo' + # rubygems 2.6.3 ensures that gem lists are sorted newest first + pos = if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.6.3") + :first + else + :last + end + # If one or more matching versions are installed, the newest of them # is the current version if !matching_installed_versions.empty? - gemspec = matching_installed_versions.last + gemspec = matching_installed_versions.send(pos) logger.debug { "#{@new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}" } gemspec # If no version matching the requirements exists, the latest installed # version is the current version. elsif !all_installed_versions.empty? - gemspec = all_installed_versions.last + gemspec = all_installed_versions.send(pos) logger.debug { "#{@new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" } gemspec else diff --git a/lib/chef/provider/package/windows/exe.rb b/lib/chef/provider/package/windows/exe.rb index 70c9879845..211845c073 100644 --- a/lib/chef/provider/package/windows/exe.rb +++ b/lib/chef/provider/package/windows/exe.rb @@ -78,12 +78,9 @@ class Chef private def uninstall_command(uninstall_string) - uninstall_string.delete!('"') + uninstall_string = "\"#{uninstall_string}\"" if ::File.exist?(uninstall_string) uninstall_string = [ - %q{/d"}, - ::File.dirname(uninstall_string), - %q{" }, - ::File.basename(uninstall_string), + uninstall_string, expand_options(new_resource.options), " ", unattended_flags, diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb index 5ee1dbea8e..e20a7332f7 100644 --- a/lib/chef/provider/package/zypper.rb +++ b/lib/chef/provider/package/zypper.rb @@ -38,15 +38,15 @@ class Chef status = shell_out_with_timeout!("zypper --non-interactive info #{package_name}") status.stdout.each_line do |line| case line - when /^Version: (.+)$/ - candidate_version = $1 - Chef::Log.debug("#{new_resource} version #{$1}") - when /^Installed: Yes$/ + when /^Version *: (.+) *$/ + candidate_version = $1.strip + Chef::Log.debug("#{new_resource} version #{candidate_version}") + when /^Installed *: Yes *$/ is_installed = true Chef::Log.debug("#{new_resource} is installed") - when /^Status: out-of-date \(version (.+) installed\)$/ - current_version = $1 - Chef::Log.debug("#{new_resource} out of date version #{$1}") + when /^Status *: out-of-date \(version (.+) installed\) *$/ + current_version = $1.strip + Chef::Log.debug("#{new_resource} out of date version #{current_version}") end end current_version = candidate_version if is_installed diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb index 6365f6a171..ab85ec35ac 100644 --- a/lib/chef/provider/powershell_script.rb +++ b/lib/chef/provider/powershell_script.rb @@ -36,7 +36,7 @@ class Chef end def command - basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory + basepath = is_forced_32bit ? wow64_directory : run_context.node["kernel"]["os_info"]["system_directory"] # Powershell.exe is always in "v1.0" folder (for backwards compatibility) interpreter_path = Chef::Util::PathHelper.join(basepath, "WindowsPowerShell", "v1.0", interpreter) diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb index e3bc579107..15b71c43bd 100644 --- a/lib/chef/provider/remote_directory.rb +++ b/lib/chef/provider/remote_directory.rb @@ -209,6 +209,8 @@ class Chef def cookbook_file_resource(target_path, relative_source_path) res = Chef::Resource::CookbookFile.new(target_path, run_context) res.cookbook_name = resource_cookbook + # Set the sensitivity level + res.sensitive(new_resource.sensitive) res.source(::File.join(source, relative_source_path)) if Chef::Platform.windows? && files_rights files_rights.each_pair do |permission, *args| diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index 2633187690..479eb0a7e2 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -260,6 +260,18 @@ class Chef end # + # Token class to hold an unresolved subscribes call with an associated + # run context. + # + # @api private + # @see Resource#subscribes + class UnresolvedSubscribes < self + # The full key ise given as the name in {Resource#subscribes} + alias_method :to_s, :name + alias_method :declared_key, :name + end + + # # Subscribes to updates from other resources, causing a particular action to # run on *this* resource when the other resource is updated. # @@ -326,7 +338,7 @@ class Chef resources = [resources].flatten resources.each do |resource| if resource.is_a?(String) - resource = Chef::Resource.new(resource, run_context) + resource = UnresolvedSubscribes.new(resource, run_context) end if resource.run_context.nil? resource.run_context = run_context @@ -1530,23 +1542,6 @@ class Chef end # @api private - def self.register_deprecated_lwrp_class(resource_class, class_name) - if Chef::Resource.const_defined?(class_name, false) - Chef::Log.warn "#{class_name} already exists! Deprecation class overwrites #{resource_class}" - Chef::Resource.send(:remove_const, class_name) - end - - if !Chef::Config[:treat_deprecation_warnings_as_errors] - Chef::Resource.const_set(class_name, resource_class) - deprecated_constants[class_name.to_sym] = resource_class - end - end - - def self.deprecated_constants - @deprecated_constants ||= {} - end - - # @api private def lookup_provider_constant(name, action = :nothing) begin self.class.provider_base.const_get(convert_to_class_name(name.to_s)) @@ -1559,6 +1554,27 @@ class Chef end end + module DeprecatedLWRPClass + + # @api private + def register_deprecated_lwrp_class(resource_class, class_name) + if Chef::Resource.const_defined?(class_name, false) + Chef::Log.warn "#{class_name} already exists! Deprecation class overwrites #{resource_class}" + Chef::Resource.send(:remove_const, class_name) + end + + if !Chef::Config[:treat_deprecation_warnings_as_errors] + Chef::Resource.const_set(class_name, resource_class) + Chef::Resource.deprecated_constants[class_name.to_sym] = resource_class + end + end + + def deprecated_constants + raise "Deprecated constants should be called only on Chef::Resource" unless self == Chef::Resource + @deprecated_constants ||= {} + end + end + private def self.remove_canonical_dsl @@ -1569,6 +1585,7 @@ class Chef end end end + extend DeprecatedLWRPClass end end diff --git a/lib/chef/resource_builder.rb b/lib/chef/resource_builder.rb index 138e401d5c..1641fe60f2 100644 --- a/lib/chef/resource_builder.rb +++ b/lib/chef/resource_builder.rb @@ -104,7 +104,11 @@ class Chef end def is_trivial_resource?(resource) - identicalish_resources?(resource_class.new(name, run_context), resource) + trivial_resource = resource_class.new(name, run_context) + # force un-lazy the name property on the created trivial resource + name_property = resource_class.properties.find { |sym, p| p.name_property? } + trivial_resource.send(name_property[0]) unless name_property.nil? + identicalish_resources?(trivial_resource, resource) end # this is an equality test specific to checking for 3694 cloning warnings @@ -124,9 +128,10 @@ class Chef end def emit_cloned_resource_warning - Chef::Log.warn("Cloning resource attributes for #{resource} from prior resource (CHEF-3694)") - Chef::Log.warn("Previous #{prior_resource}: #{prior_resource.source_line}") if prior_resource.source_line - Chef::Log.warn("Current #{resource}: #{resource.source_line}") if resource.source_line + message = "Cloning resource attributes for #{resource} from prior resource (CHEF-3694)" + message << "\nPrevious #{prior_resource}: #{prior_resource.source_line}" if prior_resource.source_line + message << "\nCurrent #{resource}: #{resource.source_line}" if resource.source_line + Chef.log_deprecation(message) end def emit_harmless_cloning_debug diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb index 29c936a932..7ef476c44b 100644 --- a/lib/chef/run_context.rb +++ b/lib/chef/run_context.rb @@ -194,12 +194,10 @@ class Chef # @param [Chef::Resource::Notification] The notification to add. # def notifies_before(notification) - nr = notification.notifying_resource - if nr.instance_of?(Chef::Resource) - before_notification_collection[nr.name] << notification - else - before_notification_collection[nr.declared_key] << notification - end + # Note for the future, notification.notifying_resource may be an instance + # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes} + # with a string value. + before_notification_collection[notification.notifying_resource.declared_key] << notification end # @@ -208,12 +206,10 @@ class Chef # @param [Chef::Resource::Notification] The notification to add. # def notifies_immediately(notification) - nr = notification.notifying_resource - if nr.instance_of?(Chef::Resource) - immediate_notification_collection[nr.name] << notification - else - immediate_notification_collection[nr.declared_key] << notification - end + # Note for the future, notification.notifying_resource may be an instance + # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes} + # with a string value. + immediate_notification_collection[notification.notifying_resource.declared_key] << notification end # @@ -222,12 +218,10 @@ class Chef # @param [Chef::Resource::Notification] The notification to add. # def notifies_delayed(notification) - nr = notification.notifying_resource - if nr.instance_of?(Chef::Resource) - delayed_notification_collection[nr.name] << notification - else - delayed_notification_collection[nr.declared_key] << notification - end + # Note for the future, notification.notifying_resource may be an instance + # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes} + # with a string value. + delayed_notification_collection[notification.notifying_resource.declared_key] << notification end # @@ -245,50 +239,29 @@ class Chef # # Get the list of before notifications sent by the given resource. # - # TODO seriously, this is actually wrong. resource.name is not unique, - # you need the type as well. - # # @return [Array[Notification]] # def before_notifications(resource) - if resource.instance_of?(Chef::Resource) - return before_notification_collection[resource.name] - else - return before_notification_collection[resource.declared_key] - end + return before_notification_collection[resource.declared_key] end # # Get the list of immediate notifications sent by the given resource. # - # TODO seriously, this is actually wrong. resource.name is not unique, - # you need the type as well. - # # @return [Array[Notification]] # def immediate_notifications(resource) - if resource.instance_of?(Chef::Resource) - return immediate_notification_collection[resource.name] - else - return immediate_notification_collection[resource.declared_key] - end + return immediate_notification_collection[resource.declared_key] end # # Get the list of delayed (end of run) notifications sent by the given # resource. # - # TODO seriously, this is actually wrong. resource.name is not unique, - # you need the type as well. - # # @return [Array[Notification]] # def delayed_notifications(resource) - if resource.instance_of?(Chef::Resource) - return delayed_notification_collection[resource.name] - else - return delayed_notification_collection[resource.declared_key] - end + return delayed_notification_collection[resource.declared_key] end # diff --git a/lib/chef/shell.rb b/lib/chef/shell.rb index aad5c49d00..26683cc25d 100644 --- a/lib/chef/shell.rb +++ b/lib/chef/shell.rb @@ -148,7 +148,7 @@ module Shell end def self.greeting - " #{Etc.getlogin}@#{Shell.session.node.fqdn}" + " #{Etc.getlogin}@#{Shell.session.node["fqdn"]}" rescue NameError, ArgumentError "" end diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 7c38be31b3..cc29ef1740 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.12.2" + VERSION = "12.13.21" end # diff --git a/omnibus/.kitchen.yml b/omnibus/.kitchen.yml index 90283f8afb..a14d3498de 100644 --- a/omnibus/.kitchen.yml +++ b/omnibus/.kitchen.yml @@ -11,7 +11,7 @@ driver: cpus: 4 memory: 4096 synced_folders: - - ['..', '/home/vagrant/chef'] + - ['../..', '/vagrant/code'] - ['../../omnibus', '/home/vagrant/omnibus'] - ['../../omnibus-software', '/home/vagrant/omnibus-software'] @@ -24,7 +24,12 @@ provisioner: attributes: vagrant: this_key_exists_so_we_have_a_vagrant_key: true - chef_omnibus_install_options: -P angrychef + omnibus: + build_user: vagrant + build_user_group: vagrant + build_user_password: vagrant + product_name: angrychef + product_version: latest chef_omnibus_root: /opt/angrychef platforms: @@ -91,12 +96,30 @@ platforms: # at `C:\vagrant\code\chef` - ['../..', '/vagrant/code'] provisioner: + attributes: + omnibus: + build_user: vagrant + build_user_group: Administrators + build_user_password: vagrant + chef_omnibus_root: /opscode/angrychef + # By adding an `i386` to the name the Omnibus cookbook's `load-omnibus-toolchain.bat` + # will load the 32-bit version of the MinGW toolchain. + - name: windows-2012r2-standard-i386 + driver: + box: chef/windows-server-2012r2-standard # private + synced_folders: + # We have to mount this repos enclosing folder as the Omnibus build + # gets cranky if the mounted ChefDK source folder is a symlink. This + # mounts at `C:\vagrant\code` and the ChefDK source folder is available + # at `C:\vagrant\code\chef-dk` + - ['../..', '/vagrant/code'] + provisioner: + attributes: + omnibus: + build_user: vagrant + build_user_group: Administrators + build_user_password: vagrant chef_omnibus_root: /opscode/angrychef - -attribute_defaults: &attribute_defaults - build_user: vagrant - build_user_group: vagrant - build_user_password: vagrant suites: # - name: angrychef @@ -109,7 +132,6 @@ suites: - name: chef attributes: omnibus: - <<: *attribute_defaults install_dir: /opt/chef run_list: - omnibus::default diff --git a/omnibus/Gemfile b/omnibus/Gemfile index ef1dec354d..3e19adfb39 100644 --- a/omnibus/Gemfile +++ b/omnibus/Gemfile @@ -13,10 +13,10 @@ gem "pedump", git: "https://github.com/ksubrama/pedump.git", branch: "patch-1" # by running `bundle install --without development` to speed up build times. group :development do # Use Berkshelf for resolving cookbook dependencies - gem "berkshelf", "~> 3.0" + gem "berkshelf", "~> 4.0" # Use Test Kitchen with Vagrant for converging the build environment - gem "test-kitchen", "~> 1.7.1" + gem "test-kitchen", "~> 1.9" gem "kitchen-vagrant", "~> 0.19.0" gem "winrm-fs", "~> 0.4.0" gem "pry" diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index f165fa5d12..f8cfeec014 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -1,13 +1,13 @@ GIT remote: https://github.com/chef/omnibus-software.git - revision: 2f04eff7dbec575cb2985d846dacd02a422cd36f + revision: 5e767a6d220f024256439b0a8415eefe923967dc specs: omnibus-software (4.0.0) omnibus (>= 5.2.0) GIT remote: https://github.com/chef/omnibus.git - revision: a36e70caedceadfcf0d85e2adef44ba0218a60a6 + revision: 29d390ec93709ceec2667038aed6769ee29a0646 specs: omnibus (5.4.0) aws-sdk (~> 2) @@ -35,35 +35,36 @@ GIT GEM remote: https://rubygems.org/ specs: - addressable (2.3.8) + addressable (2.4.0) artifactory (2.3.2) - awesome_print (1.6.1) - aws-sdk (2.3.9) - aws-sdk-resources (= 2.3.9) - aws-sdk-core (2.3.9) + awesome_print (1.7.0) + aws-sdk (2.4.2) + aws-sdk-resources (= 2.4.2) + aws-sdk-core (2.4.2) jmespath (~> 1.0) - aws-sdk-resources (2.3.9) - aws-sdk-core (= 2.3.9) - berkshelf (3.3.0) - addressable (~> 2.3.4) - berkshelf-api-client (~> 1.2) + aws-sdk-resources (2.4.2) + aws-sdk-core (= 2.4.2) + berkshelf (4.3.3) + addressable (~> 2.3, >= 2.3.4) + berkshelf-api-client (~> 2.0, >= 2.0.2) buff-config (~> 1.0) buff-extensions (~> 1.0) buff-shell_out (~> 0.1) - celluloid (~> 0.16.0) + celluloid (= 0.16.0) celluloid-io (~> 0.16.1) cleanroom (~> 1.0) - faraday (~> 0.9.0) - httpclient (~> 2.6.0) - minitar (~> 0.5.4) - octokit (~> 3.0) + faraday (~> 0.9) + httpclient (~> 2.7) + minitar (~> 0.5, >= 0.5.4) + octokit (~> 4.0) retryable (~> 2.0) - ridley (~> 4.0) - solve (~> 1.1) + ridley (~> 4.5) + solve (~> 2.0) thor (~> 0.19) - berkshelf-api-client (1.3.1) + berkshelf-api-client (2.0.2) faraday (~> 0.9.1) - httpclient (~> 2.6.0) + httpclient (~> 2.7.0) + ridley (~> 4.5) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) buff-config (1.0.1) @@ -75,31 +76,26 @@ GEM buff-shell_out (0.2.0) buff-ruby_engine (~> 0.1.0) builder (3.2.2) - byebug (9.0.4) + byebug (9.0.5) celluloid (0.16.0) timers (~> 4.0.0) celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.10.24) + chef-config (12.12.15) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) - chef-sugar (3.3.0) + chef-sugar (3.4.0) cleanroom (1.0.0) coderay (1.1.1) debug_inspector (0.0.2) - dep-selector-libgecode (1.2.0) - dep_selector (1.0.3) - dep-selector-libgecode (~> 1.0) - ffi (~> 1.9) - diff-lcs (1.2.5) erubis (2.7.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) - ffi (1.9.10) - ffi (1.9.10-x86-mingw32) - ffi-yajl (2.2.3) + ffi (1.9.14) + ffi (1.9.14-x86-mingw32) + ffi-yajl (2.3.0) libyajl2 (~> 1.2) fuzzyurl (0.8.0) gssapi (1.2.0) @@ -109,13 +105,11 @@ GEM hashie (3.4.4) hitimes (1.2.4) hitimes (1.2.4-x86-mingw32) - httpclient (2.6.0.1) + httpclient (2.7.2) iostruct (0.0.4) ipaddress (0.8.3) - jmespath (1.2.4) - json_pure (>= 1.8.1) + jmespath (1.3.1) json (1.8.3) - json_pure (1.8.3) kitchen-vagrant (0.19.0) test-kitchen (~> 1.4) libyajl2 (1.2.0) @@ -125,11 +119,8 @@ GEM multi_json (~> 1.10) method_source (0.8.2) minitar (0.5.4) - mixlib-authentication (1.4.0) + mixlib-authentication (1.4.1) mixlib-log - rspec-core (~> 3.2) - rspec-expectations (~> 3.2) - rspec-mocks (~> 3.2) mixlib-cli (1.6.0) mixlib-config (2.2.1) mixlib-install (1.0.12) @@ -142,6 +133,7 @@ GEM win32-process (~> 0.8.2) wmi-lite (~> 1.0) mixlib-versioning (1.1.0) + molinillo (0.4.5) multi_json (1.12.1) multipart-post (1.2.0) net-scp (1.2.1) @@ -149,9 +141,9 @@ GEM net-ssh (3.1.1) nio4r (1.2.1) nori (2.6.0) - octokit (3.8.0) - sawyer (~> 0.6.0, >= 0.5.3) - ohai (8.16.0) + octokit (4.3.0) + sawyer (~> 0.7.0, >= 0.5.3) + ohai (8.17.1) chef-config (>= 12.5.0.alpha.1, < 13) ffi (~> 1.9) ffi-yajl (~> 2.2) @@ -176,7 +168,7 @@ GEM binding_of_caller (>= 0.7) pry (>= 0.9.11) retryable (2.0.3) - ridley (4.4.2) + ridley (4.5.1) addressable buff-config (~> 1.0) buff-extensions (~> 1.0) @@ -184,39 +176,30 @@ GEM buff-shell_out (~> 0.1) celluloid (~> 0.16.0) celluloid-io (~> 0.16.1) - chef-config + chef-config (>= 12.5.0) erubis faraday (~> 0.9.0) hashie (>= 2.0.2, < 4.0.0) - httpclient (~> 2.6) + httpclient (~> 2.7) json (>= 1.7.7) mixlib-authentication (>= 1.3.0) retryable (~> 2.0) semverse (~> 1.1) varia_model (~> 0.4.0) - rspec-core (3.4.4) - rspec-support (~> 3.4.0) - rspec-expectations (3.4.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) - rspec-mocks (3.4.1) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.4.0) - rspec-support (3.4.1) ruby-progressbar (1.8.1) rubyntlm (0.6.0) rubyzip (1.2.0) safe_yaml (1.0.4) - sawyer (0.6.0) - addressable (~> 2.3.5) + sawyer (0.7.0) + addressable (>= 2.3.5, < 2.5) faraday (~> 0.8, < 0.10) semverse (1.2.1) slop (3.6.0) - solve (1.2.1) - dep_selector (~> 1.0) + solve (2.0.3) + molinillo (~> 0.4.2) semverse (~> 1.1) systemu (2.6.5) - test-kitchen (1.7.3) + test-kitchen (1.9.2) mixlib-install (~> 1.0, >= 1.0.4) mixlib-shellout (>= 1.2, < 3.0) net-scp (~> 1.1) @@ -252,7 +235,7 @@ PLATFORMS x86-mingw32 DEPENDENCIES - berkshelf (~> 3.0) + berkshelf (~> 4.0) kitchen-vagrant (~> 0.19.0) omnibus! omnibus-software! @@ -260,7 +243,7 @@ DEPENDENCIES pry pry-byebug pry-stack_explorer - test-kitchen (~> 1.7.1) + test-kitchen (~> 1.9) winrm-fs (~> 0.4.0) BUNDLED WITH diff --git a/omnibus/config/software/chef-gem-nokogiri.rb b/omnibus/config/software/chef-gem-nokogiri.rb index 8bf7100d5b..a25a47d341 100644 --- a/omnibus/config/software/chef-gem-nokogiri.rb +++ b/omnibus/config/software/chef-gem-nokogiri.rb @@ -8,4 +8,5 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) license "MIT" license_file "https://github.com/ruby-prof/ruby-prof/blob/master/LICENSE" +dependency "chef-gem-pkg-config" dependency "chef-gem-mini_portile2" diff --git a/omnibus/config/software/chef-gem-pkg-config.rb b/omnibus/config/software/chef-gem-pkg-config.rb new file mode 100644 index 0000000000..9c6e6fa695 --- /dev/null +++ b/omnibus/config/software/chef-gem-pkg-config.rb @@ -0,0 +1,9 @@ +# gem installs this gem from the version specified in chef's Gemfile.lock +# so we can take advantage of omnibus's caching. Just duplicate this file and +# add the new software def to chef software def if you want to separate +# another gem's installation. +require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" +BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "LGPL-2.1" +license_file "https://github.com/ruby-gnome2/pkg-config/blob/master/LGPL-2.1" diff --git a/omnibus/config/software/chef.rb b/omnibus/config/software/chef.rb index 49db136e94..c6ced7e566 100644 --- a/omnibus/config/software/chef.rb +++ b/omnibus/config/software/chef.rb @@ -69,13 +69,6 @@ build do block { log.info(log_key) { "" } } bundle "install --verbose", env: project_env - # For whatever reason, nokogiri software def deletes this (rather small) directory - block { log.info(log_key) { "" } } - block "Remove mini_portile test dir" do - mini_portile = shellout!("#{bundle_bin} show mini_portile").stdout.chomp - remove_directory File.join(mini_portile, "test") - end - # Check that it worked block { log.info(log_key) { "" } } bundle "check", env: project_env diff --git a/omnibus_overrides.rb b/omnibus_overrides.rb index d971dd99fa..7771e805e3 100644 --- a/omnibus_overrides.rb +++ b/omnibus_overrides.rb @@ -1,12 +1,12 @@ # DO NOT EDIT. Generated by "rake dependencies". Edit version_policy.rb instead. override :rubygems, version: "2.6.4" -override :bundler, version: "1.11.2" +override :bundler, version: "1.12.5" override "libffi", version: "3.2.1" override "libiconv", version: "1.14" override "liblzma", version: "5.2.2" override "libtool", version: "2.4.2" -override "libxml2", version: "2.9.3" -override "libxslt", version: "1.1.28" +override "libxml2", version: "2.9.4" +override "libxslt", version: "1.1.29" override "libyaml", version: "0.1.6" override "makedepend", version: "1.0.5" override "ncurses", version: "5.9" diff --git a/pkg.rb b/pkg.rb deleted file mode 100644 index bb26f257d0..0000000000 --- a/pkg.rb +++ /dev/null @@ -1 +0,0 @@ -package "chef-12.9.38-1.powerpc" diff --git a/rubygems-pkg/rubygems-update-2.4.6.gem b/rubygems-pkg/rubygems-update-2.4.6.gem Binary files differdeleted file mode 100644 index 97ebec693a..0000000000 --- a/rubygems-pkg/rubygems-update-2.4.6.gem +++ /dev/null diff --git a/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb b/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb index ef0967a4d2..e45e7d9f68 100644 --- a/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb +++ b/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb @@ -1,4 +1,2 @@ -set_unless[:attr_load_order] = [] -set[:attr_load_order] << "circular-dep1::default" - - +normal_unless[:attr_load_order] = [] +normal[:attr_load_order] << "circular-dep1::default" diff --git a/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb b/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb index f2ef012aa1..37f396b1f9 100644 --- a/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb +++ b/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb @@ -1,3 +1,2 @@ -set_unless[:attr_load_order] = [] -set[:attr_load_order] << "circular-dep2::default" - +normal_unless[:attr_load_order] = [] +normal[:attr_load_order] << "circular-dep2::default" diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb b/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb index e818d36a9e..3059494198 100644 --- a/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb +++ b/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb @@ -1,2 +1,2 @@ -set_unless[:attr_load_order] = [] -set[:attr_load_order] << "dependency1::aa_first" +normal_unless[:attr_load_order] = [] +normal[:attr_load_order] << "dependency1::aa_first" diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/default.rb b/spec/data/run_context/cookbooks/dependency1/attributes/default.rb index 6875274e3f..a65a3345bc 100644 --- a/spec/data/run_context/cookbooks/dependency1/attributes/default.rb +++ b/spec/data/run_context/cookbooks/dependency1/attributes/default.rb @@ -1,2 +1,2 @@ -set_unless[:attr_load_order] = [] -set[:attr_load_order] << "dependency1::default" +normal_unless[:attr_load_order] = [] +normal[:attr_load_order] << "dependency1::default" diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb b/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb index 1a513b03d4..94ffb30133 100644 --- a/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb +++ b/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb @@ -1,3 +1,2 @@ -set_unless[:attr_load_order] = [] -set[:attr_load_order] << "dependency1::zz_last" - +normal_unless[:attr_load_order] = [] +normal[:attr_load_order] << "dependency1::zz_last" diff --git a/spec/data/run_context/cookbooks/dependency2/attributes/default.rb b/spec/data/run_context/cookbooks/dependency2/attributes/default.rb index 526751f422..8917bf9730 100644 --- a/spec/data/run_context/cookbooks/dependency2/attributes/default.rb +++ b/spec/data/run_context/cookbooks/dependency2/attributes/default.rb @@ -1,3 +1,2 @@ -set_unless[:attr_load_order] = [] -set[:attr_load_order] << "dependency2::default" - +normal_unless[:attr_load_order] = [] +normal[:attr_load_order] << "dependency2::default" diff --git a/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb b/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb index 3ad2b925aa..07294665b2 100644 --- a/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb +++ b/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb @@ -1,3 +1,2 @@ -set_unless[:attr_load_order] = [] -set[:attr_load_order] << "no-default-attr::server" - +normal_unless[:attr_load_order] = [] +normal[:attr_load_order] << "no-default-attr::server" diff --git a/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb b/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb index cca56bc61f..77309462b1 100644 --- a/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb +++ b/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb @@ -1,3 +1,2 @@ -set_unless[:attr_load_order] = [] -set[:attr_load_order] << "test-with-circular-deps::default" - +normal_unless[:attr_load_order] = [] +normal[:attr_load_order] << "test-with-circular-deps::default" diff --git a/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb b/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb index 4d71cc3cfe..c4cc8151a4 100644 --- a/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb +++ b/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb @@ -1,3 +1,2 @@ -set_unless[:attr_load_order] = [] -set[:attr_load_order] << "test-with-deps::default" - +normal_unless[:attr_load_order] = [] +normal[:attr_load_order] << "test-with-deps::default" diff --git a/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg b/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg Binary files differindex a2d3e7bb4e..0295507373 100644 --- a/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg +++ b/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg diff --git a/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg b/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg Binary files differindex 9ce9445689..19782211e3 100644 --- a/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg +++ b/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg diff --git a/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg b/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg Binary files differindex 250084d3ce..360c0843e2 100644 --- a/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg +++ b/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg diff --git a/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg b/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg Binary files differindex f275c78dc6..d82b0d09bc 100644 --- a/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg +++ b/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg diff --git a/spec/functional/resource/cron_spec.rb b/spec/functional/resource/cron_spec.rb index 3380eccb0d..f5948191c5 100644 --- a/spec/functional/resource/cron_spec.rb +++ b/spec/functional/resource/cron_spec.rb @@ -120,7 +120,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do return if %w{aix solaris}.include?(ohai[:platform]) # Test if the attribute exists on newly created cron cron_should_exists(cron_name, "") - expect(shell_out("crontab -l -u #{new_resource.user} | grep \"#{attribute.upcase}=#{value}\"").exitstatus).to eq(0) + expect(shell_out("crontab -l -u #{new_resource.user} | grep '#{attribute.upcase}=\"#{value}\"'").exitstatus).to eq(0) end after do @@ -146,6 +146,13 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do new_resource.home "/home/opscode" create_and_validate_with_attribute(new_resource, "home", "/home/opscode") end + + %i{ home mailto path shell }.each do |attr| + it "supports an empty string for #{attr} attribute" do + new_resource.send(attr, "") + create_and_validate_with_attribute(new_resource, attr.to_s, "") + end + end end describe "negative tests for create action" do diff --git a/spec/functional/resource/dsc_script_spec.rb b/spec/functional/resource/dsc_script_spec.rb index dc687ec074..5ee97f04a3 100644 --- a/spec/functional/resource/dsc_script_spec.rb +++ b/spec/functional/resource/dsc_script_spec.rb @@ -469,7 +469,7 @@ EOF end it "allows the use of ps_credential" do - pending("Pended until we can adjust the test cert to meet the WMF 5 cert requirements.") + skip("Skipped until we can adjust the test cert to meet the WMF 5 cert requirements.") expect(user_exists?(dsc_user)).to eq(false) powershell_script_resource.run_action(:run) expect(File).to exist(configuration_data_path) diff --git a/spec/functional/resource/package_spec.rb b/spec/functional/resource/package_spec.rb index 6dc55f7f01..0f01a751ec 100644 --- a/spec/functional/resource/package_spec.rb +++ b/spec/functional/resource/package_spec.rb @@ -260,7 +260,7 @@ describe Chef::Resource::Package, metadata do end before do - node.set[:preseed_value] = "FROM TEMPLATE" + node.normal[:preseed_value] = "FROM TEMPLATE" end it "preseeds the package, then installs it" do diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb index f270043f2c..32529fbb0c 100644 --- a/spec/functional/resource/template_spec.rb +++ b/spec/functional/resource/template_spec.rb @@ -110,7 +110,7 @@ describe Chef::Resource::Template do context "using single helper syntax referencing @node" do before do - node.set[:helper_test_attr] = "value from helper method" + node.normal[:helper_test_attr] = "value from helper method" resource.helper(:helper_method) { "#{@node[:helper_test_attr]}" } end @@ -131,7 +131,7 @@ describe Chef::Resource::Template do context "using an inline block referencing @node" do before do - node.set[:helper_test_attr] = "value from helper method" + node.normal[:helper_test_attr] = "value from helper method" resource.helpers do def helper_method @@ -168,7 +168,7 @@ describe Chef::Resource::Template do end before do - node.set[:helper_test_attr] = "value from helper method" + node.normal[:helper_test_attr] = "value from helper method" resource.helpers(ExampleModuleReferencingATNode) end diff --git a/spec/functional/shell_spec.rb b/spec/functional/shell_spec.rb index fe2abdb12a..636162fb16 100644 --- a/spec/functional/shell_spec.rb +++ b/spec/functional/shell_spec.rb @@ -137,7 +137,7 @@ describe Shell do it "sets the override_runlist from the command line" do output, exitstatus = run_chef_shell_with("-o 'override::foo,override::bar'") do |out, keyboard| - show_recipes_code = %q[puts "#{node.recipes.inspect}"] + show_recipes_code = %q[puts "#{node["recipes"].inspect}"] keyboard.puts(show_recipes_code) read_until(out, show_recipes_code) end diff --git a/spec/integration/knife/client_bulk_delete_spec.rb b/spec/integration/knife/client_bulk_delete_spec.rb new file mode 100644 index 0000000000..a422401af6 --- /dev/null +++ b/spec/integration/knife/client_bulk_delete_spec.rb @@ -0,0 +1,130 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife client bulk delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some clients" do + before do + client "concat", {} + client "cons", {} + client "car", {} + client "cdr", {} + client "cat", {} + end + + it "deletes all matching clients" do + knife("client bulk delete ^ca.*", input: "Y").should_succeed <<EOM +The following clients will be deleted: + +car cat + +Are you sure you want to delete these clients? (Y/N) Deleted client car +Deleted client cat +EOM + + knife("client list").should_succeed <<EOM +cdr +chef-validator +chef-webui +concat +cons +EOM + end + + it "deletes all matching clients when unanchored" do + knife("client bulk delete ca.*", input: "Y").should_succeed <<EOM +The following clients will be deleted: + +car cat concat + +Are you sure you want to delete these clients? (Y/N) Deleted client car +Deleted client cat +Deleted client concat +EOM + + knife("client list").should_succeed <<EOM +cdr +chef-validator +chef-webui +cons +EOM + end + end + + when_the_chef_server "has a validator client" do + before do + client "cons", {} + client "car", {} + client "car-validator", { validator: true } + client "cdr", {} + client "cat", {} + end + + it "refuses to delete a validator normally" do + knife("client bulk delete ^ca.*", input: "Y").should_succeed <<EOM +The following clients are validators and will not be deleted: + +car-validator + +You must specify --delete-validators to delete the validator clients +The following clients will be deleted: + +car cat + +Are you sure you want to delete these clients? (Y/N) Deleted client car +Deleted client cat +EOM + + knife("client list").should_succeed <<EOM +car-validator +cdr +chef-validator +chef-webui +cons +EOM + end + + it "deletes a validator when told to" do + knife("client bulk delete ^ca.* -D", input: "Y\nY").should_succeed <<EOM +The following validators will be deleted: + +car-validator + +Are you sure you want to delete these validators? (Y/N) Deleted client car-validator +The following clients will be deleted: + +car cat + +Are you sure you want to delete these clients? (Y/N) Deleted client car +Deleted client cat +EOM + + knife("client list").should_succeed <<EOM +cdr +chef-validator +chef-webui +cons +EOM + end + end +end diff --git a/spec/integration/knife/client_create_spec.rb b/spec/integration/knife/client_create_spec.rb new file mode 100644 index 0000000000..10172833c8 --- /dev/null +++ b/spec/integration/knife/client_create_spec.rb @@ -0,0 +1,69 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "openssl" + +describe "knife client create", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let(:out) { "Created client[bah]\n" } + + when_the_chef_server "is empty" do + it "creates a new client" do + knife("client create -k bah").should_succeed stderr: out + end + + it "creates a new validator client" do + knife("client create -k --validator bah").should_succeed stderr: out + knife("client show bah").should_succeed <<EOM +admin: false +chef_type: client +name: bah +validator: true +EOM + end + + it "refuses to add an existing client" do + pending "Knife client create must not blindly overwrite an existing client" + knife("client create -k bah").should_succeed stderr: out + expect { knife("client create -k bah") }.to raise_error(Net::HTTPServerException) + end + + it "saves the private key to a file" do + Dir.mktmpdir do |tgt| + knife("client create -f #{tgt}/bah.pem bah").should_succeed stderr: out + expect(File).to exist("#{tgt}/bah.pem") + end + end + + it "reads the public key from a file" do + Dir.mktmpdir do |tgt| + key = OpenSSL::PKey::RSA.generate(1024) + File.open("#{tgt}/public.pem", "w") { |pub| pub.write(key.public_key.to_pem) } + knife("client create -p #{tgt}/public.pem bah").should_succeed stderr: out + end + end + + it "refuses to run if conflicting options are passed" do + knife("client create -p public.pem --prevent-keygen blah").should_fail stderr: "FATAL: You cannot pass --public-key and --prevent-keygen\n", stdout: /^USAGE.*/ + end + end +end diff --git a/spec/integration/knife/client_delete_spec.rb b/spec/integration/knife/client_delete_spec.rb new file mode 100644 index 0000000000..d135dd0a5b --- /dev/null +++ b/spec/integration/knife/client_delete_spec.rb @@ -0,0 +1,63 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife client delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some clients" do + before do + client "cons", {} + client "car", {} + client "car-validator", { validator: true } + client "cdr", {} + client "cat", {} + end + + it "deletes a client" do + knife("client delete car", input: "Y").should_succeed <<EOM +Do you really want to delete car? (Y/N) Deleted client[car] +EOM + + knife("client list").should_succeed <<EOM +car-validator +cat +cdr +chef-validator +chef-webui +cons +EOM + end + + it "refuses to delete a validator normally" do + knife("client delete car-validator", input: "Y").should_fail exit_code: 2, stdout: "Do you really want to delete car-validator? (Y/N) ", stderr: <<EOM +FATAL: You must specify --delete-validators to delete the validator client car-validator +EOM + end + + it "deletes a validator correctly" do + knife("client delete car-validator -D", input: "Y").should_succeed <<EOM +Do you really want to delete car-validator? (Y/N) Deleted client[car-validator] +EOM + end + + end +end diff --git a/spec/integration/knife/client_key_create_spec.rb b/spec/integration/knife/client_key_create_spec.rb new file mode 100644 index 0000000000..b588afbe50 --- /dev/null +++ b/spec/integration/knife/client_key_create_spec.rb @@ -0,0 +1,65 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "openssl" + +describe "knife client key create", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let(:out) { "Created key: new" } + + when_the_chef_server "has a client" do + before do + client "bah", {} + end + + it "creates a new client key" do + knife("client key create -k new bah").should_succeed stderr: /^#{out}/, stdout: /.*BEGIN RSA PRIVATE KEY/ + end + + it "creates a new client key with an expiration date" do + date = "2017-12-31T23:59:59Z" + knife("client key create -k new -e #{date} bah").should_succeed stderr: /^#{out}/, stdout: /.*BEGIN RSA PRIVATE KEY/ + knife("client key show bah new").should_succeed /expiration_date:.*#{date}/ + end + + it "refuses to add an already existing key" do + knife("client key create -k new bah") + expect { knife("client key create -k new bah") }.to raise_error(Net::HTTPServerException) + end + + it "saves the private key to a file" do + Dir.mktmpdir do |tgt| + knife("client key create -f #{tgt}/bah.pem -k new bah").should_succeed stderr: /^#{out}/ + expect(File).to exist("#{tgt}/bah.pem") + end + end + + it "reads the public key from a file" do + Dir.mktmpdir do |tgt| + key = OpenSSL::PKey::RSA.generate(1024) + File.open("#{tgt}/public.pem", "w") { |pub| pub.write(key.public_key.to_pem) } + knife("client key create -p #{tgt}/public.pem -k new bah").should_succeed stderr: /^#{out}/ + end + end + + end +end diff --git a/spec/integration/knife/client_key_delete_spec.rb b/spec/integration/knife/client_key_delete_spec.rb new file mode 100644 index 0000000000..d5827aa545 --- /dev/null +++ b/spec/integration/knife/client_key_delete_spec.rb @@ -0,0 +1,42 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife client key delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a client" do + before do + client "car", {} + end + + it "deletes a client" do + out = "Do you really want to delete the key named new for the client named car? (Y/N) " + knife("client key create -k new car") + knife("client key delete car new", input: "Y").should_succeed stdout: out, stderr: <<EOM +Deleted key named new for the client named car +EOM + + knife("client key list car").should_succeed "" + end + + end +end diff --git a/spec/integration/knife/client_key_list_spec.rb b/spec/integration/knife/client_key_list_spec.rb new file mode 100644 index 0000000000..de9894622e --- /dev/null +++ b/spec/integration/knife/client_key_list_spec.rb @@ -0,0 +1,60 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "date" + +describe "knife client key list", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let(:now) { DateTime.now } + let(:last_month) { (now << 1).strftime("%FT%TZ") } + let(:next_month) { (now >> 1).strftime("%FT%TZ") } + + when_the_chef_server "has a client" do + before do + client "cons", {} + knife("client key create cons -k new") + knife("client key create cons -k next_month -e #{next_month}") + knife("client key create cons -k expired -e #{last_month}") + end + + it "lists the keys for a client" do + knife("client key list cons").should_succeed "expired\nnew\nnext_month\n" + end + + it "shows detailed output" do + knife("client key list -w cons").should_succeed <<EOM +expired: http://127.0.0.1:8900/clients/cons/keys/expired (expired) +new: http://127.0.0.1:8900/clients/cons/keys/new +next_month: http://127.0.0.1:8900/clients/cons/keys/next_month +EOM + end + + it "lists the expired keys for a client" do + knife("client key list -e cons").should_succeed "expired\n" + end + + it "lists the unexpired keys for a client" do + knife("client key list -n cons").should_succeed "new\nnext_month\n" + end + + end +end diff --git a/spec/integration/knife/client_key_show_spec.rb b/spec/integration/knife/client_key_show_spec.rb new file mode 100644 index 0000000000..e96ff3b6fe --- /dev/null +++ b/spec/integration/knife/client_key_show_spec.rb @@ -0,0 +1,44 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "date" + +describe "knife client key show", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let(:now) { DateTime.now } + let(:last_month) { (now << 1).strftime("%FT%TZ") } + let(:next_month) { (now >> 1).strftime("%FT%TZ") } + + when_the_chef_server "has a client" do + before do + client "cons", {} + knife("client key create cons -k new") + knife("client key create cons -k next_month -e #{next_month}") + knife("client key create cons -k expired -e #{last_month}") + end + + it "shows a key for a client" do + knife("client key show cons new").should_succeed stdout: /.*name:.*new/ + end + + end +end diff --git a/spec/integration/knife/client_list_spec.rb b/spec/integration/knife/client_list_spec.rb new file mode 100644 index 0000000000..4159df73f1 --- /dev/null +++ b/spec/integration/knife/client_list_spec.rb @@ -0,0 +1,48 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife client list", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some clients" do + before do + client "cons", {} + client "car", {} + client "car-validator", { validator: true } + client "cdr", {} + client "cat", {} + end + + it "lists the clients" do + knife("client list").should_succeed <<EOM +car +car-validator +cat +cdr +chef-validator +chef-webui +cons +EOM + end + + end +end diff --git a/spec/integration/knife/client_show_spec.rb b/spec/integration/knife/client_show_spec.rb new file mode 100644 index 0000000000..23ac204d77 --- /dev/null +++ b/spec/integration/knife/client_show_spec.rb @@ -0,0 +1,36 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife client show", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a client" do + before do + client "cons", {} + end + + it "shows a client" do + knife("client show cons").should_succeed stdout: /.*name:.*cons/ + end + + end +end diff --git a/spec/integration/knife/cookbook_bulk_delete_spec.rb b/spec/integration/knife/cookbook_bulk_delete_spec.rb new file mode 100644 index 0000000000..4740813ce1 --- /dev/null +++ b/spec/integration/knife/cookbook_bulk_delete_spec.rb @@ -0,0 +1,64 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "chef/knife/cookbook_bulk_delete" + +describe "knife cookbook bulk delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a cookbook" do + before do + cookbook "foo", "1.0.0" + cookbook "foo", "0.6.5" + cookbook "fox", "0.6.0" + cookbook "fox", "0.6.5" + cookbook "fax", "0.6.0" + cookbook "zfa", "0.6.5" + end + + # rubocop:disable Style/TrailingWhitespace + it "knife cookbook bulk delete deletes all matching cookbooks" do + stdout = <<EOM +All versions of the following cookbooks will be deleted: + +foo fox + +Do you really want to delete these cookbooks? (Y/N) +EOM + + stderr = <<EOM +Deleted cookbook foo [1.0.0] +Deleted cookbook foo [0.6.5] +Deleted cookbook fox [0.6.5] +Deleted cookbook fox [0.6.0] +EOM + + knife("cookbook bulk delete ^fo.*", input: "Y").should_succeed(stderr: stderr, stdout: stdout) + + knife("cookbook list -a").should_succeed <<EOM +fax 0.6.0 +zfa 0.6.5 +EOM + end + # rubocop:enable Style/TrailingWhitespace + + end +end diff --git a/spec/integration/knife/cookbook_download_spec.rb b/spec/integration/knife/cookbook_download_spec.rb new file mode 100644 index 0000000000..2fbffb9dea --- /dev/null +++ b/spec/integration/knife/cookbook_download_spec.rb @@ -0,0 +1,95 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "chef/knife/cookbook_download" +require "tmpdir" + +describe "knife cookbook download", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let(:tmpdir) { Dir.mktmpdir } + + when_the_chef_server "has only one cookbook" do + before do + cookbook "x", "1.0.1" + end + + it "knife cookbook download downloads the latest version" do + knife("cookbook download -d #{tmpdir} x").should_succeed stderr: <<EOM +Downloading x cookbook version 1.0.1 +Downloading resources +Downloading providers +Downloading recipes +Downloading definitions +Downloading libraries +Downloading attributes +Downloading files +Downloading templates +Downloading root_files +Cookbook downloaded to #{tmpdir}/x-1.0.1 +EOM + end + + it "knife cookbook download with a version downloads the specified version" do + knife("cookbook download -d #{tmpdir} x 1.0.1").should_succeed stderr: <<EOM +Downloading x cookbook version 1.0.1 +Downloading resources +Downloading providers +Downloading recipes +Downloading definitions +Downloading libraries +Downloading attributes +Downloading files +Downloading templates +Downloading root_files +Cookbook downloaded to #{tmpdir}/x-1.0.1 +EOM + end + + it "knife cookbook download with an unknown version raises an error" do + expect { knife("cookbook download -d #{tmpdir} x 1.0.0") }.to raise_error(Net::HTTPServerException) + end + end + + when_the_chef_server "has multiple cookbook versions" do + before do + cookbook "x", "1.0.1" + cookbook "x", "1.0.0" + end + + it "knife cookbook download with no version prompts" do + knife("cookbook download -d #{tmpdir} x", input: "2\n").should_succeed(stderr: <<EOM, stdout: "Which version do you want to download?\n1. x 1.0.0\n2. x 1.0.1\n\n" +Downloading x cookbook version 1.0.1 +Downloading resources +Downloading providers +Downloading recipes +Downloading definitions +Downloading libraries +Downloading attributes +Downloading files +Downloading templates +Downloading root_files +Cookbook downloaded to #{tmpdir}/x-1.0.1 +EOM +) + end + end +end diff --git a/spec/integration/knife/cookbook_list_spec.rb b/spec/integration/knife/cookbook_list_spec.rb new file mode 100644 index 0000000000..65578696f2 --- /dev/null +++ b/spec/integration/knife/cookbook_list_spec.rb @@ -0,0 +1,54 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "chef/knife/cookbook_list" + +describe "knife cookbook list", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a cookbook" do + before do + cookbook "x", "1.0.0" + cookbook "x", "0.6.5" + cookbook "x", "0.6.0" + cookbook "y", "0.6.5" + cookbook "y", "0.6.0" + cookbook "z", "0.6.5" + end + + it "knife cookbook list shows all the cookbooks" do + knife("cookbook list").should_succeed <<EOM +x 1.0.0 +y 0.6.5 +z 0.6.5 +EOM + end + + it "knife cookbook list -a shows all the versions of all the cookbooks" do + knife("cookbook list -a").should_succeed <<EOM +x 1.0.0 0.6.5 0.6.0 +y 0.6.5 0.6.0 +z 0.6.5 +EOM + end + + end +end diff --git a/spec/integration/knife/cookbook_show_spec.rb b/spec/integration/knife/cookbook_show_spec.rb new file mode 100644 index 0000000000..c001d66b97 --- /dev/null +++ b/spec/integration/knife/cookbook_show_spec.rb @@ -0,0 +1,159 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "chef/knife/cookbook_show" + +describe "knife cookbook show", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a cookbook" do + before do + cookbook "x", "1.0.0", { "recipes" => { "default.rb" => "file 'n'", "x.rb" => "" } } + cookbook "x", "0.6.5" + end + + it "knife cookbook show x shows all the versions" do + knife("cookbook show x").should_succeed "x 1.0.0 0.6.5\n" + end + + # rubocop:disable Style/TrailingWhitespace + it "knife cookbook show x 1.0.0 shows the correct version" do + knife("cookbook show x 1.0.0").should_succeed <<EOM +attributes: +chef_type: cookbook_version +cookbook_name: x +definitions: +files: +frozen?: false +json_class: Chef::CookbookVersion +libraries: +metadata: + attributes: + chef_versions: + conflicting: + dependencies: + description: + gems: + groupings: + issues_url: + license: All rights reserved + long_description: + maintainer: + maintainer_email: + name: x + ohai_versions: + platforms: + privacy: false + providing: + recipes: + recommendations: + replacing: + source_url: + suggestions: + version: 1.0.0 +name: x-1.0.0 +providers: +recipes: + checksum: 4631b34cf58de10c5ef1304889941b2e + name: default.rb + path: recipes/default.rb + specificity: default + url: http://127.0.0.1:8900/file_store/checksums/4631b34cf58de10c5ef1304889941b2e + + checksum: d41d8cd98f00b204e9800998ecf8427e + name: x.rb + path: recipes/x.rb + specificity: default + url: http://127.0.0.1:8900/file_store/checksums/d41d8cd98f00b204e9800998ecf8427e +resources: +root_files: + checksum: 8226671f751ba102dea6a6b6bd32fa8d + name: metadata.rb + path: metadata.rb + specificity: default + url: http://127.0.0.1:8900/file_store/checksums/8226671f751ba102dea6a6b6bd32fa8d +templates: +version: 1.0.0 +EOM + end + + it "knife cookbook show x 1.0.0 metadata shows the metadata" do + knife("cookbook show x 1.0.0 metadata").should_succeed <<EOM +attributes: +chef_versions: +conflicting: +dependencies: +description: +gems: +groupings: +issues_url: +license: All rights reserved +long_description: +maintainer: +maintainer_email: +name: x +ohai_versions: +platforms: +privacy: false +providing: +recipes: +recommendations: +replacing: +source_url: +suggestions: +version: 1.0.0 +EOM + end + + it "knife cookbook show x 1.0.0 recipes shows all the recipes" do + knife("cookbook show x 1.0.0 recipes").should_succeed <<EOM +checksum: 4631b34cf58de10c5ef1304889941b2e +name: default.rb +path: recipes/default.rb +specificity: default +url: http://127.0.0.1:8900/file_store/checksums/4631b34cf58de10c5ef1304889941b2e + +checksum: d41d8cd98f00b204e9800998ecf8427e +name: x.rb +path: recipes/x.rb +specificity: default +url: http://127.0.0.1:8900/file_store/checksums/d41d8cd98f00b204e9800998ecf8427e +EOM + end + # rubocop:enable Style/TrailingWhitespace + + it "knife cookbook show x 1.0.0 recipes default.rb shows the default recipe" do + knife("cookbook show x 1.0.0 recipes default.rb").should_succeed "file 'n'\n" + end + + it "knife cookbook show with a non-existent file displays an error" do + expect { knife("cookbook show x 1.0.0 recipes moose.rb") }.to raise_error(Chef::Exceptions::FileNotFound) + end + + it "knife cookbook show with a non-existent version displays an error" do + expect { knife("cookbook show x 1.0.1") }.to raise_error(Net::HTTPServerException) + end + + it "knife cookbook show with a non-existent cookbook displays an error" do + expect { knife("cookbook show y") }.to raise_error(Net::HTTPServerException) + end + end +end diff --git a/spec/integration/knife/cookbook_upload_spec.rb b/spec/integration/knife/cookbook_upload_spec.rb new file mode 100644 index 0000000000..a0de725603 --- /dev/null +++ b/spec/integration/knife/cookbook_upload_spec.rb @@ -0,0 +1,90 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "chef/knife/cookbook_upload" + +describe "knife cookbook upload", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let (:cb_dir) { "#{@repository_dir}/cookbooks" } + + when_the_chef_server "is empty" do + when_the_repository "has a cookbook" do + before do + file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0") + end + + it "knife cookbook upload uploads the cookbook" do + knife("cookbook upload x -o #{cb_dir}").should_succeed stderr: <<EOM +Uploading x [1.0.0] +Uploaded 1 cookbook. +EOM + end + + it "knife cookbook upload --freeze uploads and freezes the cookbook" do + knife("cookbook upload x -o #{cb_dir} --freeze").should_succeed stderr: <<EOM +Uploading x [1.0.0] +Uploaded 1 cookbook. +EOM + # Modify the file, attempt to reupload + file "cookbooks/x/metadata.rb", 'name "x"; version "1.0.0"#different' + knife("cookbook upload x -o #{cb_dir} --freeze").should_fail stderr: <<EOM +Uploading x [1.0.0] +ERROR: Version 1.0.0 of cookbook x is frozen. Use --force to override. +WARNING: Not updating version constraints for x in the environment as the cookbook is frozen. +ERROR: Failed to upload 1 cookbook. +EOM + end + end + + when_the_repository "has a cookbook that depends on another cookbook" do + before do + file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "\ndepends 'y'") + file "cookbooks/y/metadata.rb", cb_metadata("y", "1.0.0") + end + + it "knife cookbook upload --include-dependencies uploads both cookbooks" do + knife("cookbook upload --include-dependencies x -o #{cb_dir}").should_succeed stderr: <<EOM +Uploading x [1.0.0] +Uploading y [1.0.0] +Uploaded 2 cookbooks. +EOM + end + + it "knife cookbook upload fails due to missing dependencies" do + knife("cookbook upload x -o #{cb_dir}").should_fail stderr: <<EOM +Uploading x [1.0.0] +ERROR: Cookbook x depends on cookbooks which are not currently +ERROR: being uploaded and cannot be found on the server. +ERROR: The missing cookbook(s) are: 'y' version '>= 0.0.0' +EOM + end + + it "knife cookbook upload -a uploads both cookbooks" do + knife("cookbook upload -a -o #{cb_dir}").should_succeed stderr: <<EOM +Uploading x [1.0.0] +Uploading y [1.0.0] +Uploaded all cookbooks. +EOM + end + end + end +end diff --git a/spec/integration/knife/data_bag_create_spec.rb b/spec/integration/knife/data_bag_create_spec.rb new file mode 100644 index 0000000000..0a07792dbc --- /dev/null +++ b/spec/integration/knife/data_bag_create_spec.rb @@ -0,0 +1,58 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "chef/knife/data_bag_create" + +describe "knife data bag create", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let(:err) { "Created data_bag[foo]\n" } + let(:out) { "Created data_bag_item[bar]\n" } + let(:exists) { "Data bag foo already exists\n" } + + when_the_chef_server "is empty" do + it "creates a new data bag" do + knife("data bag create foo").should_succeed stderr: err + 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 + + it "refuses to add an existing data bag" do + knife("data bag create foo").should_succeed stderr: err + knife("data bag create foo").should_succeed stderr: exists + 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 + end +end diff --git a/spec/integration/knife/data_bag_delete_spec.rb b/spec/integration/knife/data_bag_delete_spec.rb new file mode 100644 index 0000000000..96345b0d2b --- /dev/null +++ b/spec/integration/knife/data_bag_delete_spec.rb @@ -0,0 +1,58 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "chef/knife/data_bag_delete" + +describe "knife data bag delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some data bags" do + before do + data_bag "x", {} + data_bag "canteloupe", {} + data_bag "rocket", { "falcon9" => { heavy: "true" }, "atlas" => {}, "ariane" => {} } + end + + it "with an empty data bag" do + knife("data bag delete canteloupe", input: "y").should_succeed <<EOM +Do you really want to delete canteloupe? (Y/N) Deleted data_bag[canteloupe] +EOM + end + + it "with a bag with some items" do + knife("data bag delete rocket", input: "y").should_succeed <<EOM +Do you really want to delete rocket? (Y/N) Deleted data_bag[rocket] +EOM + end + + it "with a single item" do + knife("data bag delete rocket falcon9", input: "y").should_succeed <<EOM +Do you really want to delete falcon9? (Y/N) Deleted data_bag_item[falcon9] +EOM + end + + it "choosing not to delete" do + knife("data bag delete rocket falcon9", input: "n").should_succeed <<EOM, exit_code: 3 +Do you really want to delete falcon9? (Y/N) You said no, so I'm done here. +EOM + end + end +end diff --git a/spec/integration/knife/data_bag_from_file_spec.rb b/spec/integration/knife/data_bag_from_file_spec.rb new file mode 100644 index 0000000000..ca8f743487 --- /dev/null +++ b/spec/integration/knife/data_bag_from_file_spec.rb @@ -0,0 +1,115 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife data bag from file", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let (:db_dir) { "#{@repository_dir}/data_bags" } + + when_the_chef_server "has an empty data bag" do + before do + data_bag "foo", {} + data_bag "bar", {} + end + + when_the_repository "has some data bag items" do + before do + file "data_bags/foo/bar.json", { "id" => "bar", "foo" => "bar " } + file "data_bags/foo/bzr.json", { "id" => "bzr", "foo" => "bar " } + file "data_bags/foo/cat.json", { "id" => "cat", "foo" => "bar " } + file "data_bags/foo/dog.json", { "id" => "dog", "foo" => "bar " } + file "data_bags/foo/encrypted.json", <<EOM +{ + "id": "encrypted", + "password": { + "encrypted_data": "H6ab5RY9a9JAkS8A0RCMspXtOJh0ai8cNeA4Q3gLO8s=\\n", + "iv": "uWKKKxrJgtELlGMCOLJdkA==\\n", + "version": 1, + "cipher": "aes-256-cbc" + } +} +EOM + file "data_bags/bar/round_trip.json", <<EOM +{ + "name": "data_bag_item_bar_round_trip", + "json_class": "Chef::DataBagItem", + "chef_type": "data_bag_item", + "data_bag": "bar", + "raw_data": { + "id": "round_trip", + "root_password": { + "encrypted_data": "noDOsTpsTAZlTU5sprhmYZzUDfr8du7hH/zRDOjRAmoTJHTZyfYoR221EOOW\\nXJ1D\\n", + "iv": "Bnqhfy6n0Hx1wCe9pxHLoA==\\n", + "version": 1, + "cipher": "aes-256-cbc" + }, + "admin_password": { + "encrypted_data": "TcC7dU1gx6OnE5Ab4i/k42UEf0Nnr7cAyuTHId/LNjNOwpNf7XZc27DQSjuy\\nHPlt\\n", + "iv": "+TAWJuPWCI2+WB8lGJAyvw==\\n", + "version": 1, + "cipher": "aes-256-cbc" + } + } +} +EOM + end + + it "uploads a single file" do + knife("data bag from file foo #{db_dir}/foo/bar.json").should_succeed stderr: <<EOM +Updated data_bag_item[foo::bar] +EOM + end + + it "uploads a single encrypted file" do + knife("data bag from file foo #{db_dir}/foo/encrypted.json").should_succeed stderr: <<EOM +Updated data_bag_item[foo::encrypted] +EOM + end + + it "uploads a file in chef's internal format" do + pending "chef/chef#4815" + knife("data bag from file bar #{db_dir}/bar/round_trip.json").should_succeed stderr: <<EOM +Updated data_bag_item[bar::round_trip] +EOM + end + + it "uploads many files" do + knife("data bag from file foo #{db_dir}/foo/bar.json #{db_dir}/foo/bzr.json").should_succeed stderr: <<EOM +Updated data_bag_item[foo::bar] +Updated data_bag_item[foo::bzr] +EOM + end + + it "uploads a whole directory" do + knife("data bag from file foo #{db_dir}/foo") + knife("data bag show foo").should_succeed <<EOM +bar +bzr +cat +dog +encrypted +EOM + end + + end + end +end diff --git a/spec/integration/knife/data_bag_list_spec.rb b/spec/integration/knife/data_bag_list_spec.rb new file mode 100644 index 0000000000..7db9638660 --- /dev/null +++ b/spec/integration/knife/data_bag_list_spec.rb @@ -0,0 +1,43 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "chef/knife/data_bag_list" + +describe "knife data bag list", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some data bags" do + before do + data_bag "x", {} + data_bag "canteloupe", {} + data_bag "rocket", {} + end + + it "knife data bag list shows all the cookbooks" do + knife("data bag list").should_succeed <<EOM +canteloupe +rocket +x +EOM + end + + end +end diff --git a/spec/integration/knife/data_bag_show_spec.rb b/spec/integration/knife/data_bag_show_spec.rb new file mode 100644 index 0000000000..22381adb9e --- /dev/null +++ b/spec/integration/knife/data_bag_show_spec.rb @@ -0,0 +1,53 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "chef/knife/data_bag_show" + +describe "knife data bag show", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some data bags" do + before do + data_bag "x", {} + data_bag "canteloupe", {} + data_bag "rocket", { "falcon9" => { heavy: "true" }, "atlas" => {}, "ariane" => {} } + end + + it "with an empty data bag" do + knife("data bag show canteloupe").should_succeed "\n" + end + + it "with a bag with some items" do + knife("data bag show rocket").should_succeed <<EOM +ariane +atlas +falcon9 +EOM + end + + it "with a single item" do + knife("data bag show rocket falcon9").should_succeed <<EOM, stderr: "WARNING: Unencrypted data bag detected, ignoring any provided secret options.\n" +heavy: true +id: falcon9 +EOM + end + end +end diff --git a/spec/integration/knife/diff_spec.rb b/spec/integration/knife/diff_spec.rb index b7d2f4d1c3..b3bd23f48e 100644 --- a/spec/integration/knife/diff_spec.rb +++ b/spec/integration/knife/diff_spec.rb @@ -72,7 +72,7 @@ EOM file "data_bags/x/y.json", {} file "environments/_default.json", { "description" => "The default Chef environment" } file "environments/x.json", {} - file "nodes/x.json", {} + file "nodes/x.json", { "normal" => { "tags" => [] } } file "roles/x.json", {} file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY } file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY } @@ -366,7 +366,7 @@ EOM file "data_bags/x/y.json", {} file "environments/_default.json", { "description" => "The default Chef environment" } file "environments/x.json", {} - file "nodes/x.json", {} + file "nodes/x.json", { "normal" => { "tags" => [] } } file "roles/x.json", {} file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY } file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY } diff --git a/spec/integration/knife/download_spec.rb b/spec/integration/knife/download_spec.rb index a924226ea2..be0fc9d708 100644 --- a/spec/integration/knife/download_spec.rb +++ b/spec/integration/knife/download_spec.rb @@ -76,7 +76,7 @@ EOM file "data_bags/x/y.json", {} file "environments/_default.json", { "description" => "The default Chef environment" } file "environments/x.json", {} - file "nodes/x.json", {} + file "nodes/x.json", { "normal" => { "tags" => [] } } file "roles/x.json", {} file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY } file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY } @@ -645,7 +645,7 @@ EOM file "data_bags/x/y.json", {} file "environments/_default.json", { "description" => "The default Chef environment" } file "environments/x.json", {} - file "nodes/x.json", {} + file "nodes/x.json", { "normal" => { "tags" => [] } } file "roles/x.json", {} file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY } file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY } @@ -1271,7 +1271,7 @@ EOM file "groups/x.json", {} file "invitations.json", [ "foo" ] file "members.json", [ "bar" ] - file "nodes/x.json", {} + file "nodes/x.json", { "normal" => { "tags" => [] } } file "org.json", { "full_name" => "Something" } file "policies/x-1.0.0.json", {} file "policies/blah-1.0.0.json", {} @@ -1295,7 +1295,7 @@ EOM file "environments/x.json", { "description" => "foo" } file "groups/x.json", { "description" => "foo" } file "groups/x.json", { "groups" => [ "admin" ] } - file "nodes/x.json", { "run_list" => [ "blah" ] } + file "nodes/x.json", { "normal" => { "tags" => [] }, "run_list" => [ "blah" ] } file "org.json", { "full_name" => "Something Else " } file "policies/x-1.0.0.json", { "run_list" => [ "blah" ] } file "policy_groups/x.json", { diff --git a/spec/integration/knife/environment_compare_spec.rb b/spec/integration/knife/environment_compare_spec.rb new file mode 100644 index 0000000000..3259b27d1b --- /dev/null +++ b/spec/integration/knife/environment_compare_spec.rb @@ -0,0 +1,74 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife environment compare", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some environments" do + before do + cookbook "blah", "1.0.1" + cookbook "blah", "1.1.1" + cookbook "krad", "1.1.1" + environment "x", { + "cookbook_versions" => { + "blah" => "= 1.0.0", + "krad" => ">= 1.0.0", + }, + } + environment "y", { + "cookbook_versions" => { + "blah" => "= 1.1.0", + "krad" => ">= 1.0.0", + }, + } + end + + # rubocop:disable Style/TrailingWhitespace + it "displays the cookbooks for a single environment" do + knife("environment compare x").should_succeed <<EOM + x +blah = 1.0.0 +krad >= 1.0.0 + +EOM + end + + it "compares the cookbooks for two environments" do + knife("environment compare x y").should_succeed <<EOM + x y +blah = 1.0.0 = 1.1.0 +krad >= 1.0.0 >= 1.0.0 + +EOM + end + + it "compares the cookbooks for all environments" do + knife("environment compare --all").should_succeed <<EOM + x y +blah = 1.0.0 = 1.1.0 +krad >= 1.0.0 >= 1.0.0 + +EOM + end + # rubocop:enable Style/TrailingWhitespace + end +end diff --git a/spec/integration/knife/environment_create_spec.rb b/spec/integration/knife/environment_create_spec.rb new file mode 100644 index 0000000000..03fd4e63f7 --- /dev/null +++ b/spec/integration/knife/environment_create_spec.rb @@ -0,0 +1,40 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife environment create", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let(:out) { "Created bah\n" } + + when_the_chef_server "is empty" do + it "creates a new environment" do + knife("environment create bah").should_succeed out + end + + it "refuses to add an existing environment" do + pending "Knife environment create must not blindly overwrite an existing environment" + knife("environment create bah").should_succeed out + expect { knife("environment create bah") }.to raise_error(Net::HTTPServerException) + end + + end +end diff --git a/spec/integration/knife/environment_delete_spec.rb b/spec/integration/knife/environment_delete_spec.rb new file mode 100644 index 0000000000..0f1fe5c4a8 --- /dev/null +++ b/spec/integration/knife/environment_delete_spec.rb @@ -0,0 +1,36 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife environment delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has an environment" do + before do + environment "y", {} + end + + it "deletes an environment" do + knife("environment delete y", input: "y").should_succeed "Do you really want to delete y? (Y/N) Deleted y\n" + end + + end +end diff --git a/spec/integration/knife/environment_from_file_spec.rb b/spec/integration/knife/environment_from_file_spec.rb new file mode 100644 index 0000000000..67d4373939 --- /dev/null +++ b/spec/integration/knife/environment_from_file_spec.rb @@ -0,0 +1,115 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife environment from file", :workstation do + include IntegrationSupport + include KnifeSupport + + # include_context "default config options" + + let (:env_dir) { "#{@repository_dir}/environments" } + + when_the_chef_server "is empty" do + when_the_repository "has some environments" do + before do + + file "environments/cons.json", <<EOM +{ + "name": "cons", + "description": "An environment", + "cookbook_versions": { + + }, + "json_class": "Chef::Environment", + "chef_type": "environment", + "default_attributes": { + "hola": "Amigos!" + }, + "override_attributes": { + + } +} +EOM + + file "environments/car.json", <<EOM +{ + "name": "car", + "description": "An environment for list nodes", + "cookbook_versions": { + + }, + "json_class": "Chef::Environment", + "chef_type": "environment", + "default_attributes": { + "hola": "Amigos!" + }, + "override_attributes": { + + } +} +EOM + + file "environments/cdr.json", <<EOM +{ + "name": "cdr", + "description": "An environment for last nodes", + "cookbook_versions": { + + }, + "json_class": "Chef::Environment", + "chef_type": "environment", + "default_attributes": { + "hola": "Amigos!" + }, + "override_attributes": { + + } +} +EOM + + end + + it "uploads a single file" do + knife("environment from file #{env_dir}/cons.json").should_succeed stderr: <<EOM +Updated Environment cons +EOM + end + + it "uploads many files" do + knife("environment from file #{env_dir}/cons.json #{env_dir}/car.json #{env_dir}/cdr.json").should_succeed stderr: <<EOM +Updated Environment cons +Updated Environment car +Updated Environment cdr +EOM + end + + it "uploads all environments in the repository" do + cwd(".") + knife("environment from file --all") + knife("environment list").should_succeed <<EOM +_default +car +cdr +cons +EOM + end + + end + end +end diff --git a/spec/integration/knife/environment_list_spec.rb b/spec/integration/knife/environment_list_spec.rb new file mode 100644 index 0000000000..5e74453d1f --- /dev/null +++ b/spec/integration/knife/environment_list_spec.rb @@ -0,0 +1,41 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife environment list", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some environments" do + before do + environment "b", {} + environment "y", {} + end + + it "lists all the environments" do + knife("environment list").should_succeed <<EOM +_default +b +y +EOM + end + + end +end diff --git a/spec/integration/knife/environment_show_spec.rb b/spec/integration/knife/environment_show_spec.rb new file mode 100644 index 0000000000..e74bf6d05d --- /dev/null +++ b/spec/integration/knife/environment_show_spec.rb @@ -0,0 +1,56 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife environment show", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some environments" do + before do + environment "b", { + "default_attributes" => { "foo" => "bar" }, + } + end + + # rubocop:disable Style/TrailingWhitespace + it "shows an environment" do + knife("environment show b").should_succeed <<EOM +chef_type: environment +cookbook_versions: +default_attributes: + foo: bar +description: +json_class: Chef::Environment +name: b +override_attributes: +EOM + end + # rubocop:enable Style/TrailingWhitespace + + it "shows the requested attribute of an environment" do + pending "KnifeSupport doesn't appear to pass this through correctly" + knife("environment show b -a foo").should_succeed <<EOM +b: + foo: bar +EOM + end + end +end diff --git a/spec/integration/knife/node_bulk_delete_spec.rb b/spec/integration/knife/node_bulk_delete_spec.rb new file mode 100644 index 0000000000..fa706cbd2b --- /dev/null +++ b/spec/integration/knife/node_bulk_delete_spec.rb @@ -0,0 +1,51 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife node bulk delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some nodes" do + before do + node "cons", {} + node "car", {} + node "cdr", {} + node "cat", {} + end + + it "deletes all matching nodes" do + knife("node bulk delete ^ca.*", input: "Y").should_succeed <<EOM +The following nodes will be deleted: + +car cat + +Are you sure you want to delete these nodes? (Y/N) Deleted node car +Deleted node cat +EOM + + knife("node list").should_succeed <<EOM +cdr +cons +EOM + end + end + +end diff --git a/spec/integration/knife/node_create_spec.rb b/spec/integration/knife/node_create_spec.rb new file mode 100644 index 0000000000..93a2f9ce6f --- /dev/null +++ b/spec/integration/knife/node_create_spec.rb @@ -0,0 +1,46 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" +require "openssl" + +describe "knife node create", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let(:out) { "Created node[bah]\n" } + + when_the_chef_server "is empty" do + it "creates a new node" do + knife("node create bah").should_succeed out + end + + it "creates a new validator node" do + knife("node create bah").should_succeed out + knife("node show bah").should_succeed /Node Name: bah/ + end + + it "refuses to add an existing node" do + pending "Knife node create must not blindly overwrite an existing node" + knife("node create bah").should_succeed out + expect { knife("node create bah") }.to raise_error(Net::HTTPServerException) + end + + end +end diff --git a/spec/integration/knife/node_delete_spec.rb b/spec/integration/knife/node_delete_spec.rb new file mode 100644 index 0000000000..5d88af6d4f --- /dev/null +++ b/spec/integration/knife/node_delete_spec.rb @@ -0,0 +1,47 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife node delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some nodes" do + before do + node "cons", {} + node "car", {} + node "cdr", {} + node "cat", {} + end + + it "deletes a node" do + knife("node delete car", input: "Y").should_succeed <<EOM +Do you really want to delete car? (Y/N) Deleted node[car] +EOM + + knife("node list").should_succeed <<EOM +cat +cdr +cons +EOM + end + + end +end diff --git a/spec/integration/knife/node_environment_set_spec.rb b/spec/integration/knife/node_environment_set_spec.rb new file mode 100644 index 0000000000..6dadecf76a --- /dev/null +++ b/spec/integration/knife/node_environment_set_spec.rb @@ -0,0 +1,42 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife node environment set", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a node and an environment" do + before do + node "cons", {} + environment "lisp", {} + end + + it "sets an environment on a node" do + knife("node environment set cons lisp").should_succeed /chef_environment:.*lisp/ + knife("node show cons -a chef_environment").should_succeed /Environment:.*lisp/ + end + + it "with no environment" do + knife("node environment set adam").should_fail stderr: "FATAL: You must specify a node name and an environment.\n", + stdout: /^USAGE: knife node environment set NODE ENVIRONMENT\n/ + end + end +end diff --git a/spec/integration/knife/node_from_file_spec.rb b/spec/integration/knife/node_from_file_spec.rb new file mode 100644 index 0000000000..3430967a21 --- /dev/null +++ b/spec/integration/knife/node_from_file_spec.rb @@ -0,0 +1,58 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife node from file", :workstation do + include IntegrationSupport + include KnifeSupport + + # include_context "default config options" + + let (:node_dir) { "#{@repository_dir}/nodes" } + + when_the_chef_server "is empty" do + when_the_repository "has some nodes" do + before do + + file "nodes/cons.json", <<EOM +{ + "name": "cons", + "chef_environment": "_default", + "run_list": [ + "recipe[cons]" +] +, + "normal": { + "tags": [ + + ] + } +} +EOM + + end + + it "uploads a single file" do + knife("node from file #{node_dir}/cons.json").should_succeed stderr: <<EOM +Updated Node cons +EOM + end + + end + end +end diff --git a/spec/integration/knife/node_list_spec.rb b/spec/integration/knife/node_list_spec.rb new file mode 100644 index 0000000000..76f5861e03 --- /dev/null +++ b/spec/integration/knife/node_list_spec.rb @@ -0,0 +1,44 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife node list", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some nodes" do + before do + node "cons", {} + node "car", {} + node "cdr", {} + node "cat", {} + end + + it "lists all cookbooks" do + knife("node list").should_succeed <<EOM +car +cat +cdr +cons +EOM + end + + end +end diff --git a/spec/integration/knife/node_run_list_add_spec.rb b/spec/integration/knife/node_run_list_add_spec.rb new file mode 100644 index 0000000000..87d08e1975 --- /dev/null +++ b/spec/integration/knife/node_run_list_add_spec.rb @@ -0,0 +1,53 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife node run list add", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a node with no run_list" do + before do + node "cons", {} + end + + it "sets the run list" do + knife("node run list add cons recipe[foo]").should_succeed /run_list:\s*recipe\[foo\]\n/ + end + end + + when_the_chef_server "has a node with a run_list" do + before do + node "cons", { run_list: ["recipe[bar]"] } + end + + it "appends to the run list" do + knife("node run list add cons recipe[foo]").should_succeed /run_list:\n\s*recipe\[bar\]\n\s*recipe\[foo\]\n/m + end + + it "adds to the run list before the specified item" do + knife("node run list add cons -b recipe[bar] recipe[foo]").should_succeed /run_list:\n\s*recipe\[foo\]\n\s*recipe\[bar\]\n/m + end + + it "adds to the run list after the specified item" do + knife("node run list add cons -a recipe[bar] recipe[foo]").should_succeed /run_list:\n\s*recipe\[bar\]\n\s*recipe\[foo\]\n/m + end + end +end diff --git a/spec/integration/knife/node_run_list_remove_spec.rb b/spec/integration/knife/node_run_list_remove_spec.rb new file mode 100644 index 0000000000..e85e3ed8e8 --- /dev/null +++ b/spec/integration/knife/node_run_list_remove_spec.rb @@ -0,0 +1,35 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife node run list remove", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a node with a run_list" do + before do + node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] } + end + + it "removes the item from the run list" do + knife("node run list remove cons recipe[bar]").should_succeed /run_list:\s*recipe\[foo\]\n/m + end + end +end diff --git a/spec/integration/knife/node_run_list_set_spec.rb b/spec/integration/knife/node_run_list_set_spec.rb new file mode 100644 index 0000000000..ec6b08fb97 --- /dev/null +++ b/spec/integration/knife/node_run_list_set_spec.rb @@ -0,0 +1,40 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife node run list set", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a node with a run_list" do + before do + node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] } + end + + it "sets the run list" do + knife("node run list set cons recipe[bar]").should_succeed /run_list:\s*recipe\[bar\]\n/m + end + + it "with no role or recipe" do + knife("node run list set cons").should_fail stderr: "FATAL: You must supply both a node name and a run list.\n", + stdout: /^USAGE: knife node run_list set NODE ENTRIES \(options\)/m + end + end +end diff --git a/spec/integration/knife/node_show_spec.rb b/spec/integration/knife/node_show_spec.rb new file mode 100644 index 0000000000..dd890aed59 --- /dev/null +++ b/spec/integration/knife/node_show_spec.rb @@ -0,0 +1,35 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife node show", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has a node with a run_list" do + before do + node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] } + end + + it "shows the node" do + knife("node show cons").should_succeed /Run List:\s*recipe\[bar\], recipe\[foo\]\n/m + end + end +end diff --git a/spec/integration/knife/raw_spec.rb b/spec/integration/knife/raw_spec.rb index 9078bf09a1..5e0d3a3d11 100644 --- a/spec/integration/knife/raw_spec.rb +++ b/spec/integration/knife/raw_spec.rb @@ -49,7 +49,9 @@ describe "knife raw", :workstation do }, "normal": { + "tags": [ + ] }, "default": { diff --git a/spec/integration/knife/role_bulk_delete_spec.rb b/spec/integration/knife/role_bulk_delete_spec.rb new file mode 100644 index 0000000000..0e7ff941e2 --- /dev/null +++ b/spec/integration/knife/role_bulk_delete_spec.rb @@ -0,0 +1,51 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife role bulk delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some roles" do + before do + role "cons", {} + role "car", {} + role "cdr", {} + role "cat", {} + end + + it "deletes all matching roles" do + knife("role bulk delete ^ca.*", input: "Y").should_succeed <<EOM +The following roles will be deleted: + +car cat + +Are you sure you want to delete these roles? (Y/N) Deleted role car +Deleted role cat +EOM + + knife("role list").should_succeed <<EOM +cdr +cons +EOM + end + + end +end diff --git a/spec/integration/knife/role_create_spec.rb b/spec/integration/knife/role_create_spec.rb new file mode 100644 index 0000000000..941eaf5cb6 --- /dev/null +++ b/spec/integration/knife/role_create_spec.rb @@ -0,0 +1,40 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife role create", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + let(:out) { "Created role[bah]\n" } + + when_the_chef_server "is empty" do + it "creates a new role" do + knife("role create bah").should_succeed out + end + + it "refuses to add an existing role" do + pending "Knife role create must not blindly overwrite an existing role" + knife("role create bah").should_succeed out + expect { knife("role create bah") }.to raise_error(Net::HTTPServerException) + end + + end +end diff --git a/spec/integration/knife/role_delete_spec.rb b/spec/integration/knife/role_delete_spec.rb new file mode 100644 index 0000000000..9fbd3758b9 --- /dev/null +++ b/spec/integration/knife/role_delete_spec.rb @@ -0,0 +1,47 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife role delete", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some roles" do + before do + role "cons", {} + role "car", {} + role "cdr", {} + role "cat", {} + end + + it "deletes a role" do + knife("role delete car", input: "Y").should_succeed <<EOM +Do you really want to delete car? (Y/N) Deleted role[car] +EOM + + knife("role list").should_succeed <<EOM +cat +cdr +cons +EOM + end + + end +end diff --git a/spec/integration/knife/role_from_file_spec.rb b/spec/integration/knife/role_from_file_spec.rb new file mode 100644 index 0000000000..60caa3fa88 --- /dev/null +++ b/spec/integration/knife/role_from_file_spec.rb @@ -0,0 +1,95 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife role from file", :workstation do + include IntegrationSupport + include KnifeSupport + + # include_context "default config options" + + let (:role_dir) { "#{@repository_dir}/roles" } + + when_the_chef_server "is empty" do + when_the_repository "has some roles" do + before do + + file "roles/cons.json", <<EOM +{ + "name": "cons", + "description": "An role", + "json_class": "Chef::role", + "chef_type": "role", + "default_attributes": { + "hola": "Amigos!" + }, + "override_attributes": { + + } +} +EOM + + file "roles/car.json", <<EOM +{ + "name": "car", + "description": "A role for list nodes", + "json_class": "Chef::Role", + "chef_type": "role", + "default_attributes": { + "hola": "Amigos!" + }, + "override_attributes": { + + } +} +EOM + + file "roles/cdr.json", <<EOM +{ + "name": "cdr", + "description": "A role for last nodes", + "json_class": "Chef::Role", + "chef_type": "role", + "default_attributes": { + "hola": "Amigos!" + }, + "override_attributes": { + + } +} +EOM + + end + + it "uploads a single file" do + knife("role from file #{role_dir}/cons.json").should_succeed stderr: <<EOM +Updated Role cons +EOM + end + + it "uploads many files" do + knife("role from file #{role_dir}/cons.json #{role_dir}/car.json #{role_dir}/cdr.json").should_succeed stderr: <<EOM +Updated Role cons +Updated Role car +Updated Role cdr +EOM + end + + end + end +end diff --git a/spec/integration/knife/role_list_spec.rb b/spec/integration/knife/role_list_spec.rb new file mode 100644 index 0000000000..36dc76be4c --- /dev/null +++ b/spec/integration/knife/role_list_spec.rb @@ -0,0 +1,44 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife role list", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some roles" do + before do + role "cons", {} + role "car", {} + role "cdr", {} + role "cat", {} + end + + it "lists all cookbooks" do + knife("role list").should_succeed <<EOM +car +cat +cdr +cons +EOM + end + + end +end diff --git a/spec/integration/knife/role_show_spec.rb b/spec/integration/knife/role_show_spec.rb new file mode 100644 index 0000000000..df2572447c --- /dev/null +++ b/spec/integration/knife/role_show_spec.rb @@ -0,0 +1,50 @@ +# +# Copyright:: Copyright 2013-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 "support/shared/integration/integration_helper" +require "support/shared/context/config" + +describe "knife role show", :workstation do + include IntegrationSupport + include KnifeSupport + + include_context "default config options" + + when_the_chef_server "has some roles" do + before do + role "cons", {} + role "car", {} + role "cdr", {} + role "cat", {} + end + + # rubocop:disable Style/TrailingWhitespace + it "shows a cookbook" do + knife("role show cons").should_succeed <<EOM +chef_type: role +default_attributes: +description: +env_run_lists: +json_class: Chef::Role +name: cons +override_attributes: +run_list: +EOM + end + # rubocop:enable Style/TrailingWhitespace + + end +end diff --git a/spec/integration/knife/upload_spec.rb b/spec/integration/knife/upload_spec.rb index 55c95b59b7..1db0ea8726 100644 --- a/spec/integration/knife/upload_spec.rb +++ b/spec/integration/knife/upload_spec.rb @@ -96,7 +96,7 @@ EOM file "data_bags/x/y.json", {} file "environments/_default.json", { "description" => "The default Chef environment" } file "environments/x.json", {} - file "nodes/x.json", {} + file "nodes/x.json", { "normal" => { "tags" => [] } } file "roles/x.json", {} file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY } file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY } @@ -802,7 +802,7 @@ EOM file "data_bags/x/y.json", {} file "environments/_default.json", { "description" => "The default Chef environment" } file "environments/x.json", {} - file "nodes/x.json", {} + file "nodes/x.json", { "normal" => { "tags" => [] } } file "roles/x.json", {} file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY } file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY } @@ -1313,7 +1313,7 @@ EOM file "invitations.json", [ "foo" ] file "members.json", [ "bar" ] file "org.json", { "full_name" => "wootles" } - file "nodes/x.json", {} + file "nodes/x.json", { "normal" => { "tags" => [] } } file "policies/x-1.0.0.json", {} file "policies/blah-1.0.0.json", {} file "policy_groups/x.json", { "policies" => { "x" => { "revision_id" => "1.0.0" }, "blah" => { "revision_id" => "1.0.0" } } } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2f6747c9af..dcbc34a157 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -66,6 +66,8 @@ require "chef/util/file_edit" require "chef/config" +require "chef/chef_fs/file_system_cache" + if ENV["CHEF_FIPS"] == "1" Chef::Config.init_openssl end @@ -152,6 +154,8 @@ RSpec.configure do |config| config.filter_run_excluding :unix_only => true unless unix? config.filter_run_excluding :aix_only => true unless aix? config.filter_run_excluding :debian_family_only => true unless debian_family? + config.filter_run_excluding :linux_only => true unless linux? + config.filter_run_excluding :non_linux_only => true if linux? config.filter_run_excluding :supports_cloexec => true unless supports_cloexec? config.filter_run_excluding :selinux_only => true unless selinux_enabled? config.filter_run_excluding :requires_root => true unless root? @@ -200,6 +204,8 @@ RSpec.configure do |config| config.before(:each) do Chef.reset! + Chef::ChefFS::FileSystemCache.instance.reset! + Chef::Config.reset # By default, treat deprecation warnings as errors in tests. diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb index 9ba56a15e3..783429161a 100644 --- a/spec/support/platform_helpers.rb +++ b/spec/support/platform_helpers.rb @@ -138,6 +138,10 @@ def freebsd? !!(RUBY_PLATFORM =~ /freebsd/) end +def linux? + !!(RUBY_PLATFORM =~ /linux/) +end + def debian_family? !!(ohai[:platform_family] == "debian") end diff --git a/spec/support/shared/integration/knife_support.rb b/spec/support/shared/integration/knife_support.rb index 1e81cd115c..01df9ab1a7 100644 --- a/spec/support/shared/integration/knife_support.rb +++ b/spec/support/shared/integration/knife_support.rb @@ -20,10 +20,11 @@ require "chef/knife" require "chef/application/knife" require "logger" require "chef/log" +require "chef/chef_fs/file_system_cache" module KnifeSupport DEBUG = ENV["DEBUG"] - def knife(*args) + def knife(*args, input: nil) # Allow knife('role from file roles/blah.json') rather than requiring the # arguments to be split like knife('role', 'from', 'file', 'roles/blah.json') # If any argument will have actual spaces in it, the long form is required. @@ -37,7 +38,7 @@ module KnifeSupport Chef::Config[:concurrency] = 1 # Work on machines where we can't access /var - checksums_cache_dir = Dir.mktmpdir("checksums") do |checksums_cache_dir| + Dir.mktmpdir("checksums") do |checksums_cache_dir| Chef::Config[:cache_options] = { :path => checksums_cache_dir, :skip_expires => true, @@ -47,6 +48,13 @@ module KnifeSupport # ourselves, thank you very much stdout = StringIO.new stderr = StringIO.new + + stdin = if input + StringIO.new(input) + else + STDIN + end + old_loggers = Chef::Log.loggers old_log_level = Chef::Log.level begin @@ -57,12 +65,15 @@ module KnifeSupport instance = subcommand_class.new(args) # Capture stdout/stderr - instance.ui = Chef::Knife::UI.new(stdout, stderr, STDIN, {}) + instance.ui = Chef::Knife::UI.new(stdout, stderr, stdin, disable_editing: true) # Don't print stuff Chef::Config[:verbosity] = ( DEBUG ? 2 : 0 ) instance.config[:config_file] = File.join(CHEF_SPEC_DATA, "null_config.rb") + # Ensure the ChefFS cache is empty + Chef::ChefFS::FileSystemCache.instance.reset! + # Configure chef with a (mostly) blank knife.rb # We set a global and then mutate it in our stub knife.rb so we can be # extra sure that we're not loading someone's real knife.rb and then diff --git a/spec/support/shared/unit/provider/file.rb b/spec/support/shared/unit/provider/file.rb index cb539ffbc3..ee3438da70 100644 --- a/spec/support/shared/unit/provider/file.rb +++ b/spec/support/shared/unit/provider/file.rb @@ -683,6 +683,31 @@ shared_examples_for Chef::Provider::File do end end + context "do_resolv_conf_fixup" do + %w{/resolv.conf /etc/resolv.con /etc/foo/resolv.conf /c/resolv.conf}.each do |path| + context "when managing #{path}" do + let(:resource_path) { path } + it "does not reload the nameservers" do + expect(Resolv::DefaultResolver).not_to receive(:replace_resolvers) + provider.send(:do_resolv_conf_fixup) + end + end + end + context "when managing /etc/resolv.conf", linux_only: true do + let(:resource_path) { "/etc/resolv.conf" } + it "reloads the nameservers on linux" do + expect(Resolv::DefaultResolver).to receive(:replace_resolvers) + provider.send(:do_resolv_conf_fixup) + end + end + context "when managing /etc/resolv.conf", non_linux_only: true do + let(:resource_path) { "/etc/resolv.conf" } + it "does not reload the nameservers on non-linux" do + expect(Resolv::DefaultResolver).not_to receive(:replace_resolvers) + provider.send(:do_resolv_conf_fixup) + end + end + end end context "action delete" do diff --git a/spec/unit/application/solo_spec.rb b/spec/unit/application/solo_spec.rb index bb29261f5a..686ae745d8 100644 --- a/spec/unit/application/solo_spec.rb +++ b/spec/unit/application/solo_spec.rb @@ -120,9 +120,10 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config expect(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile) expect(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file) - shellout = instance_double("Mixlib::ShellOut", run_command: nil, error!: nil, stdout: "") + archive = double(Mixlib::Archive) - expect(app).to receive(:shell_out!).with("tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo").and_return(shellout) + expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive) + expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ }) app.reconfigure expect(target_file.string).to eq("remote_tarball_content") end @@ -136,11 +137,10 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks" expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true) - allow(Chef::Mixin::Command).to receive(:run_command).and_return(true) + archive = double(Mixlib::Archive) - shellout = instance_double("Mixlib::ShellOut", run_command: nil, error!: nil, stdout: "") - - expect(app).to receive(:shell_out!).with("tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo").and_return(shellout) + expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive) + expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ }) expect(app).to receive(:fetch_recipe_tarball).ordered expect(Chef::ConfigFetcher).to receive(:new).ordered.and_return(config_fetcher) app.reconfigure @@ -212,6 +212,13 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config end end + it "sets the repo path" do + expect(Chef::Config).to receive(:find_chef_repo_path).and_return("/var/chef") + app.reconfigure + expect(Chef::Config.has_key?(:chef_repo_path)).to be_truthy + expect(Chef::Config[:chef_repo_path]).to eq ("/var/chef") + end + it "runs chef-client in local mode" do allow(app).to receive(:setup_application).and_return(true) allow(app).to receive(:run_application).and_return(true) diff --git a/spec/unit/chef_fs/file_system/repository/directory_spec.rb b/spec/unit/chef_fs/file_system/repository/directory_spec.rb index e050181be9..6e53e52966 100644 --- a/spec/unit/chef_fs/file_system/repository/directory_spec.rb +++ b/spec/unit/chef_fs/file_system/repository/directory_spec.rb @@ -76,6 +76,7 @@ describe Chef::ChefFS::FileSystem::Repository::Directory do context "#create_child" do it "creates a new TestFile" do expect(TestFile).to receive(:new).with("test_child", test_directory).and_return(file_double) + allow(file_double).to receive(:file_path).and_return("#{test_directory}/test_child") expect(file_double).to receive(:write).with("test") test_directory.create_child("test_child", "test") end diff --git a/spec/unit/cookbook_version_spec.rb b/spec/unit/cookbook_version_spec.rb index 7def2f5749..81ea161bfe 100644 --- a/spec/unit/cookbook_version_spec.rb +++ b/spec/unit/cookbook_version_spec.rb @@ -120,8 +120,8 @@ describe Chef::CookbookVersion do # Used to test file-specificity related file lookups let(:node) do Chef::Node.new.tap do |n| - n.set[:platform] = "ubuntu" - n.set[:platform_version] = "13.04" + n.normal[:platform] = "ubuntu" + n.normal[:platform_version] = "13.04" n.name("testing") end end @@ -203,8 +203,8 @@ describe Chef::CookbookVersion do # Used to test file-specificity related file lookups let(:node) do Chef::Node.new.tap do |n| - n.set[:platform] = "ubuntu" - n.set[:platform_version] = "13.04" + n.normal[:platform] = "ubuntu" + n.normal[:platform_version] = "13.04" n.name("testing") end end diff --git a/spec/unit/data_collector/messages/helpers_spec.rb b/spec/unit/data_collector/messages/helpers_spec.rb index 0ed0f6c921..26f7dbacfa 100644 --- a/spec/unit/data_collector/messages/helpers_spec.rb +++ b/spec/unit/data_collector/messages/helpers_spec.rb @@ -171,20 +171,16 @@ describe Chef::DataCollector::Messages::Helpers do end describe '#update_metadata' do - let(:metadata) { double("metadata") } - it "updates the file" do allow(TestMessage).to receive(:metadata_filename).and_return("fake_metadata_file.json") - allow(TestMessage).to receive(:metadata).and_return(metadata) - expect(metadata).to receive(:[]=).with("new_key", "new_value") - expect(metadata).to receive(:to_json).and_return("metadata_json") + allow(TestMessage).to receive(:metadata).and_return({ "key" => "current_value" }) expect(Chef::FileCache).to receive(:store).with( "fake_metadata_file.json", - "metadata_json", + '{"key":"updated_value"}', 0644 ) - TestMessage.update_metadata("new_key", "new_value") + TestMessage.update_metadata("key", "updated_value") end end end diff --git a/spec/unit/data_collector/messages_spec.rb b/spec/unit/data_collector/messages_spec.rb index dee86a3907..b0c7e692d2 100644 --- a/spec/unit/data_collector/messages_spec.rb +++ b/spec/unit/data_collector/messages_spec.rb @@ -18,6 +18,7 @@ # require "spec_helper" +require "ffi_yajl" require "chef/data_collector/messages/helpers" describe Chef::DataCollector::Messages do @@ -61,13 +62,14 @@ describe Chef::DataCollector::Messages do end describe '#run_end_message' do - let(:run_status) { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) } - let(:resource1) { double("resource1", for_json: "resource_data", status: "updated") } - let(:resource2) { double("resource2", for_json: "resource_data", status: "skipped") } + let(:node) { Chef::Node.new } + let(:run_status) { Chef::RunStatus.new(node, Chef::EventDispatch::Dispatcher.new) } + let(:report1) { double("report1", report_data: { "status" => "updated" }) } + let(:report2) { double("report2", report_data: { "status" => "skipped" }) } let(:reporter_data) do { run_status: run_status, - completed_resources: [resource1, resource2], + resources: [report1, report2], } end @@ -76,6 +78,20 @@ describe Chef::DataCollector::Messages do allow(run_status).to receive(:end_time).and_return(Time.now) end + it "includes a valid node object in the payload" do + message = Chef::DataCollector::Messages.run_end_message(reporter_data) + expect(message["node"]).to be_an_instance_of(Chef::Node) + end + + it "returns a sane JSON representation of the node object" do + node.chef_environment = "my_test_environment" + node.run_list.add("recipe[my_test_cookbook::default]") + message = FFI_Yajl::Parser.parse(Chef::DataCollector::Messages.run_end_message(reporter_data).to_json) + + expect(message["node"]["chef_environment"]).to eq("my_test_environment") + expect(message["node"]["run_list"]).to eq(["recipe[my_test_cookbook::default]"]) + end + context "when the run was successful" do let(:required_fields) do %w{ @@ -86,6 +102,7 @@ describe Chef::DataCollector::Messages do expanded_run_list message_type message_version + node node_name organization_name resources @@ -136,6 +153,7 @@ describe Chef::DataCollector::Messages do expanded_run_list message_type message_version + node node_name organization_name resources @@ -169,46 +187,4 @@ describe Chef::DataCollector::Messages do end end end - - describe '#node_update_message' do - let(:run_status) { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) } - - let(:required_fields) do - %w{ - entity_name - entity_type - entity_uuid - id - message_type - message_version - organization_name - recorded_at - remote_hostname - requestor_name - requestor_type - run_id - service_hostname - source - task - user_agent - } - end - let(:optional_fields) { %w{data} } - - it "is not missing any required fields" do - missing_fields = required_fields.select do |key| - !Chef::DataCollector::Messages.node_update_message(run_status).key?(key) - end - - expect(missing_fields).to eq([]) - end - - it "does not have any extra fields" do - extra_fields = Chef::DataCollector::Messages.node_update_message(run_status).keys.select do |key| - !required_fields.include?(key) && !optional_fields.include?(key) - end - - expect(extra_fields).to eq([]) - end - end end diff --git a/spec/unit/data_collector_spec.rb b/spec/unit/data_collector_spec.rb index 78b0aaf97d..131d6b8df9 100644 --- a/spec/unit/data_collector_spec.rb +++ b/spec/unit/data_collector_spec.rb @@ -20,6 +20,7 @@ require "spec_helper" require "chef/data_collector" +require "chef/resource_builder" describe Chef::DataCollector do describe ".register_reporter?" do @@ -155,6 +156,10 @@ describe Chef::DataCollector::Reporter do let(:reporter) { described_class.new } let(:run_status) { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) } + before do + Chef::Config[:data_collector][:server_url] = "http://my-data-collector-server.mycompany.com" + end + describe '#run_started' do before do allow(reporter).to receive(:update_run_status) @@ -193,10 +198,32 @@ describe Chef::DataCollector::Reporter do end end + describe '#converge_start' do + it "stashes the run_context for later use" do + reporter.converge_start("test_context") + expect(reporter.run_context).to eq("test_context") + end + end + + describe '#converge_complete' do + it "detects and processes any unprocessed resources" do + expect(reporter).to receive(:detect_unprocessed_resources) + reporter.converge_complete + end + end + + describe '#converge_failed' do + it "detects and processes any unprocessed resources" do + expect(reporter).to receive(:detect_unprocessed_resources) + reporter.converge_failed("exception") + end + end + describe '#resource_current_state_loaded' do let(:new_resource) { double("new_resource") } let(:action) { double("action") } let(:current_resource) { double("current_resource") } + let(:resource_report) { double("resource_report") } context "when resource is a nested resource" do it "does not update the resource report" do @@ -207,14 +234,12 @@ describe Chef::DataCollector::Reporter do end context "when resource is not a nested resource" do - it "updates the resource report" do + it "creates the resource report and stores it as the current one" do allow(reporter).to receive(:nested_resource?).and_return(false) - expect(Chef::DataCollector::ResourceReport).to receive(:new).with( - new_resource, - action, - current_resource) - .and_return("resource_report") - expect(reporter).to receive(:update_current_resource_report).with("resource_report") + expect(reporter).to receive(:create_resource_report) + .with(new_resource, action, current_resource) + .and_return(resource_report) + expect(reporter).to receive(:update_current_resource_report).with(resource_report) reporter.resource_current_state_loaded(new_resource, action, current_resource) end end @@ -256,7 +281,7 @@ describe Chef::DataCollector::Reporter do before do allow(reporter).to receive(:nested_resource?) - allow(reporter).to receive(:current_resource_report).and_return(resource_report) + allow(reporter).to receive(:create_resource_report).and_return(resource_report) allow(resource_report).to receive(:skipped) end @@ -269,13 +294,12 @@ describe Chef::DataCollector::Reporter do end context "when the resource is not a nested resource" do - it "updates the resource report" do + it "creates the resource report and stores it as the current one" do allow(reporter).to receive(:nested_resource?).and_return(false) - expect(Chef::DataCollector::ResourceReport).to receive(:new).with( - new_resource, - action) - .and_return("resource_report") - expect(reporter).to receive(:update_current_resource_report).with("resource_report") + expect(reporter).to receive(:create_resource_report) + .with(new_resource, action) + .and_return(resource_report) + expect(reporter).to receive(:update_current_resource_report).with(resource_report) reporter.resource_skipped(new_resource, action, conditional) end @@ -349,15 +373,16 @@ describe Chef::DataCollector::Reporter do let(:resource_report) { double("resource_report") } before do - allow(reporter).to receive(:add_completed_resource) allow(reporter).to receive(:update_current_resource_report) + allow(reporter).to receive(:add_resource_report) + allow(reporter).to receive(:current_resource_report) allow(resource_report).to receive(:finish) end context "when there is no current resource report" do - it "does not add the updated resource" do + it "does not touch the current resource report" do allow(reporter).to receive(:current_resource_report).and_return(nil) - expect(reporter).not_to receive(:add_completed_resource) + expect(reporter).not_to receive(:update_current_resource_report) reporter.resource_completed(new_resource) end end @@ -368,9 +393,9 @@ describe Chef::DataCollector::Reporter do end context "when the resource is a nested resource" do - it "does not add the updated resource" do + it "does not mark the resource as finished" do allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true) - expect(reporter).not_to receive(:add_completed_resource) + expect(resource_report).not_to receive(:finish) reporter.resource_completed(new_resource) end end @@ -385,11 +410,6 @@ describe Chef::DataCollector::Reporter do reporter.resource_completed(new_resource) end - it "adds the resource to the updated resource list" do - expect(reporter).to receive(:add_completed_resource).with(resource_report) - reporter.resource_completed(new_resource) - end - it "nils out the current resource report" do expect(reporter).to receive(:update_current_resource_report).with(nil) reporter.resource_completed(new_resource) @@ -474,7 +494,8 @@ describe Chef::DataCollector::Reporter do [ Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse, - Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError ].each do |exception_class| + Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError, + Errno::EHOSTDOWN ].each do |exception_class| context "when the block raises a #{exception_class} exception" do it "disables the reporter" do expect(reporter).to receive(:disable_data_collector_reporter) @@ -499,4 +520,39 @@ describe Chef::DataCollector::Reporter do end end end + + describe '#validate_data_collector_server_url!' do + context "when server_url is empty" do + it "raises an exception" do + Chef::Config[:data_collector][:server_url] = "" + expect { reporter.send(:validate_data_collector_server_url!) }.to raise_error(Chef::Exceptions::ConfigurationError) + end + end + + context "when server_url is not empty" do + context "when server_url is an invalid URI" do + it "raises an exception" do + Chef::Config[:data_collector][:server_url] = "this is not a URI" + expect { reporter.send(:validate_data_collector_server_url!) }.to raise_error(Chef::Exceptions::ConfigurationError) + end + end + + context "when server_url is a valid URI" do + context "when server_url is a URI with no host" do + it "raises an exception" do + Chef::Config[:data_collector][:server_url] = "/file/uri.txt" + expect { reporter.send(:validate_data_collector_server_url!) }.to raise_error(Chef::Exceptions::ConfigurationError) + end + + end + + context "when server_url is a URI with a valid host" do + it "does not an exception" do + Chef::Config[:data_collector][:server_url] = "http://www.google.com/data-collector" + expect { reporter.send(:validate_data_collector_server_url!) }.not_to raise_error + end + end + end + end + end end diff --git a/spec/unit/dsl/declare_resource_spec.rb b/spec/unit/dsl/declare_resource_spec.rb index 6dd9317c21..692e57d2e1 100644 --- a/spec/unit/dsl/declare_resource_spec.rb +++ b/spec/unit/dsl/declare_resource_spec.rb @@ -235,6 +235,36 @@ describe Chef::ResourceCollection do ).to eql(resource) expect(run_context.resource_collection.all_resources.size).to eql(0) end + + it "removes pending delayed notifications" do + recipe.declare_resource(:zen_master, "one") + recipe.declare_resource(:zen_master, "two") do + notifies :win, "zen_master[one]" + end + recipe.delete_resource(:zen_master, "two") + resource = recipe.declare_resource(:zen_master, "two") + expect(resource.delayed_notifications).to eql([]) + end + + it "removes pending immediate notifications" do + recipe.declare_resource(:zen_master, "one") + recipe.declare_resource(:zen_master, "two") do + notifies :win, "zen_master[one]", :immediate + end + recipe.delete_resource(:zen_master, "two") + resource = recipe.declare_resource(:zen_master, "two") + expect(resource.immediate_notifications).to eql([]) + end + + it "removes pending before notifications" do + recipe.declare_resource(:zen_master, "one") + recipe.declare_resource(:zen_master, "two") do + notifies :win, "zen_master[one]", :before + end + recipe.delete_resource(:zen_master, "two") + resource = recipe.declare_resource(:zen_master, "two") + expect(resource.before_notifications).to eql([]) + end end describe "run_context helpers" do diff --git a/spec/unit/knife/cookbook_create_spec.rb b/spec/unit/knife/cookbook_create_spec.rb index 5e343f3db1..f860a8bce8 100644 --- a/spec/unit/knife/cookbook_create_spec.rb +++ b/spec/unit/knife/cookbook_create_spec.rb @@ -22,6 +22,7 @@ require "tmpdir" describe Chef::Knife::CookbookCreate do before(:each) do Chef::Config[:node_name] = "webmonkey.example.com" + Chef::Config[:treat_deprecation_warnings_as_errors] = false @knife = Chef::Knife::CookbookCreate.new @knife.config = {} @knife.name_args = ["foobar"] diff --git a/spec/unit/knife/cookbook_site_download_spec.rb b/spec/unit/knife/cookbook_site_download_spec.rb index d283bd417f..0ab6a8a9b4 100644 --- a/spec/unit/knife/cookbook_site_download_spec.rb +++ b/spec/unit/knife/cookbook_site_download_spec.rb @@ -38,6 +38,7 @@ describe Chef::Knife::CookbookSiteDownload do expect(@noauth_rest).to receive(:get). with("#{@cookbook_api_url}/apache2"). and_return(@current_data) + @knife.configure_chef end context "when the cookbook is deprecated and not forced" do diff --git a/spec/unit/knife/cookbook_site_install_spec.rb b/spec/unit/knife/cookbook_site_install_spec.rb index d60443d779..1549245ea3 100644 --- a/spec/unit/knife/cookbook_site_install_spec.rb +++ b/spec/unit/knife/cookbook_site_install_spec.rb @@ -23,6 +23,7 @@ describe Chef::Knife::CookbookSiteInstall do let(:stdout) { StringIO.new } let(:stderr) { StringIO.new } let(:downloader) { Hash.new } + let(:archive) { double(Mixlib::Archive, extract: true) } let(:repo) { double(:sanity_check => true, :reset_to_default_state => true, :prepare_to_import => true, :finalize_updates_to => true, :merge_updates_from => true) } @@ -48,6 +49,7 @@ describe Chef::Knife::CookbookSiteInstall do allow(File).to receive(:unlink) allow(File).to receive(:rmtree) allow(knife).to receive(:shell_out!).and_return(true) + allow(Mixlib::Archive).to receive(:new).and_return(archive) # CookbookSiteDownload Stup allow(knife).to receive(:download_cookbook_to).and_return(downloader) diff --git a/spec/unit/knife/cookbook_site_share_spec.rb b/spec/unit/knife/cookbook_site_share_spec.rb index 9339114d2a..5ac48539e4 100644 --- a/spec/unit/knife/cookbook_site_share_spec.rb +++ b/spec/unit/knife/cookbook_site_share_spec.rb @@ -83,11 +83,11 @@ describe Chef::Knife::CookbookSiteShare do @knife.run end - it "should print error and exit when given only 1 argument and cannot determine category" do + it "should use a default category when given only 1 argument and cannot determine category" do @knife.name_args = ["cookbook_name"] - expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name").and_return(@bad_category_response) - expect(@knife.ui).to receive(:fatal) - expect { @knife.run }.to raise_error(SystemExit) + expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name") { raise Net::HTTPServerException.new("404 Not Found", OpenStruct.new(code: "404")) } + expect(@knife).to receive(:do_upload) + expect { @knife.run }.to_not raise_error end it "should print error and exit when given only 1 argument and Chef::ServerAPI throws an exception" do diff --git a/spec/unit/knife/node_environment_set_spec.rb b/spec/unit/knife/node_environment_set_spec.rb index 03fc764fd8..7ceafdad78 100644 --- a/spec/unit/knife/node_environment_set_spec.rb +++ b/spec/unit/knife/node_environment_set_spec.rb @@ -52,29 +52,5 @@ describe Chef::Knife::NodeEnvironmentSet do @knife.run end - describe "with no environment" do - # Set up outputs for inspection later - before(:each) do - @stdout = StringIO.new - @stderr = StringIO.new - - allow(@knife.ui).to receive(:stdout).and_return(@stdout) - allow(@knife.ui).to receive(:stderr).and_return(@stderr) - end - - it "should exit" do - @knife.name_args = [ "adam" ] - expect { @knife.run }.to raise_error SystemExit - end - - it "should show the user the usage and an error" do - @knife.name_args = [ "adam" ] - - begin ; @knife.run ; rescue SystemExit ; end - - expect(@stdout.string).to eq "USAGE: knife node environment set NODE ENVIRONMENT\n" - expect(@stderr.string).to eq "FATAL: You must specify a node name and an environment.\n" - end - end end end diff --git a/spec/unit/knife/node_run_list_set_spec.rb b/spec/unit/knife/node_run_list_set_spec.rb index 1bbaa7d9a5..bd55edb997 100644 --- a/spec/unit/knife/node_run_list_set_spec.rb +++ b/spec/unit/knife/node_run_list_set_spec.rb @@ -111,30 +111,5 @@ describe Chef::Knife::NodeRunListSet do end end - describe "with no role or recipe" do - # Set up outputs for inspection later - before(:each) do - @stdout = StringIO.new - @stderr = StringIO.new - - allow(@knife.ui).to receive(:stdout).and_return(@stdout) - allow(@knife.ui).to receive(:stderr).and_return(@stderr) - end - - it "should exit" do - @knife.name_args = [ "adam" ] - expect { @knife.run }.to raise_error SystemExit - end - - it "should show the user" do - @knife.name_args = [ "adam" ] - - begin ; @knife.run ; rescue SystemExit ; end - - expect(@stdout.string).to eq "USAGE: knife node run_list set NODE ENTRIES (options)\n" - expect(@stderr.string).to eq "FATAL: You must supply both a node name and a run list.\n" - end - end - end end diff --git a/spec/unit/node/attribute_spec.rb b/spec/unit/node/attribute_spec.rb index 57ad3c0c25..e40f454c0b 100644 --- a/spec/unit/node/attribute_spec.rb +++ b/spec/unit/node/attribute_spec.rb @@ -218,7 +218,7 @@ describe Chef::Node::Attribute do end it "gives the value at each level of precedence for a path spec" do - expected = [["set_unless_enabled?", false], + expected = [ %w{default default}, %w{env_default env_default}, %w{role_default role_default}, @@ -417,12 +417,6 @@ describe Chef::Node::Attribute do expect(@attributes.normal["foo"]["bar"]).to eq(:baz) end - it "should optionally skip setting the value if one already exists" do - @attributes.set_unless_value_present = true - @attributes.normal["hostname"] = "bar" - expect(@attributes["hostname"]).to eq("latte") - end - it "does not support ||= when setting" do # This is a limitation of auto-vivification. # Users who need this behavior can use set_unless and friends @@ -493,6 +487,7 @@ describe Chef::Node::Attribute do end it "should return true if an attribute exists but is set to nil using dot notation" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false expect(@attributes.music.deeper.has_key?("gates_of_ishtar")).to eq(true) end @@ -533,10 +528,12 @@ describe Chef::Node::Attribute do describe "method_missing" do it "should behave like a [] lookup" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false expect(@attributes.music.mastodon).to eq("rocks") end it "should allow the last method to set a value if it has an = sign on the end" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false @attributes.normal.music.mastodon = %w{dream still shining} expect(@attributes.normal.music.mastodon).to eq(%w{dream still shining}) end @@ -577,7 +574,7 @@ describe Chef::Node::Attribute do it "should yield lower if we go deeper" do collect = Array.new - @attributes.one.keys.each do |k| + @attributes["one"].keys.each do |k| collect << k end expect(collect.include?("two")).to eq(true) @@ -587,7 +584,7 @@ describe Chef::Node::Attribute do end it "should not raise an exception if one of the hashes has a nil value on a deep lookup" do - expect { @attributes.place.keys { |k| } }.not_to raise_error + expect { @attributes["place"].keys { |k| } }.not_to raise_error end end @@ -1171,6 +1168,7 @@ describe Chef::Node::Attribute do end it "raises an error when using `attr=value`" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false expect { @attributes.new_key = "new value" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) end diff --git a/spec/unit/node/immutable_collections_spec.rb b/spec/unit/node/immutable_collections_spec.rb index f57ed459cd..fe4e50d1bd 100644 --- a/spec/unit/node/immutable_collections_spec.rb +++ b/spec/unit/node/immutable_collections_spec.rb @@ -95,6 +95,10 @@ describe Chef::Node::ImmutableMash do :replace, :select!, :shift, + :write, + :write!, + :unlink, + :unlink!, ].each do |mutator| it "doesn't allow mutation via `#{mutator}'" do expect { @immutable_mash.send(mutator) }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) diff --git a/spec/unit/node/vivid_mash_spec.rb b/spec/unit/node/vivid_mash_spec.rb new file mode 100644 index 0000000000..5319ba4a35 --- /dev/null +++ b/spec/unit/node/vivid_mash_spec.rb @@ -0,0 +1,377 @@ +# +# 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" +require "chef/node/attribute_collections" + +describe Chef::Node::VividMash do + class Root + attr_accessor :top_level_breadcrumb + end + + let(:root) { Root.new } + + 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 } + ) + 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 + 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 "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") + 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).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") + end + 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 }) + end + 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 }) + end + end +end diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index 923b488f72..72731c927f 100644 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -233,60 +233,69 @@ describe Chef::Node do end it "should let you go deep with attribute?" do - node.set["battles"]["people"]["wonkey"] = true + node.normal["battles"]["people"]["wonkey"] = true expect(node["battles"]["people"].attribute?("wonkey")).to eq(true) expect(node["battles"]["people"].attribute?("snozzberry")).to eq(false) end it "does not allow you to set an attribute via method_missing" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false expect { node.sunshine = "is bright" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification) 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" expect(node.sunshine).to eql("is bright") end describe "normal attributes" do it "should allow you to set an attribute with set, without pre-declaring a hash" do - node.set[:snoopy][:is_a_puppy] = true + node.normal[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end + it "should allow you to set an attribute with set_unless with method_missing but emit a deprecation warning" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false + node.normal_unless.snoopy.is_a_puppy = false + expect(node[:snoopy][:is_a_puppy]).to eq(false) + end + it "should allow you to set an attribute with set_unless" do - node.set_unless[:snoopy][:is_a_puppy] = false + node.normal_unless[:snoopy][:is_a_puppy] = false expect(node[:snoopy][:is_a_puppy]).to eq(false) end it "should not allow you to set an attribute with set_unless if it already exists" do - node.set[:snoopy][:is_a_puppy] = true - node.set_unless[:snoopy][:is_a_puppy] = false + node.normal[:snoopy][:is_a_puppy] = true + node.normal_unless[:snoopy][:is_a_puppy] = false expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should allow you to set an attribute with set_unless if is a nil value" do node.attributes.normal = { snoopy: { is_a_puppy: nil } } - node.set_unless[:snoopy][:is_a_puppy] = false + node.normal_unless[:snoopy][:is_a_puppy] = false expect(node[:snoopy][:is_a_puppy]).to eq(false) end it "should allow you to set a value after a set_unless" do # this tests for set_unless_present state bleeding between statements CHEF-3806 - node.set_unless[:snoopy][:is_a_puppy] = false - node.set[:snoopy][:is_a_puppy] = true + node.normal_unless[:snoopy][:is_a_puppy] = false + node.normal[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "should let you set a value after a 'dangling' set_unless" do # this tests for set_unless_present state bleeding between statements CHEF-3806 - node.set[:snoopy][:is_a_puppy] = "what" - node.set_unless[:snoopy][:is_a_puppy] - node.set[:snoopy][:is_a_puppy] = true + node.normal[:snoopy][:is_a_puppy] = "what" + node.normal_unless[:snoopy][:is_a_puppy] + node.normal[:snoopy][:is_a_puppy] = true expect(node[:snoopy][:is_a_puppy]).to eq(true) end it "auto-vivifies attributes created via method syntax" do - node.set.fuu.bahrr.baz = "qux" + Chef::Config[:treat_deprecation_warnings_as_errors] = false + node.normal.fuu.bahrr.baz = "qux" expect(node.fuu.bahrr.baz).to eq("qux") end @@ -295,6 +304,20 @@ describe Chef::Node do node.tag("three", "four") expect(node["tags"]).to eq(%w{one two three four}) end + + it "set is a deprecated alias for normal" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false + expect(Chef).to receive(:log_deprecation).with(/set is deprecated/) + node.set[:snoopy][:is_a_puppy] = true + expect(node[:snoopy][:is_a_puppy]).to eq(true) + end + + it "set_unless is a deprecated alias for normal_unless" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false + expect(Chef).to receive(:log_deprecation).with(/set_unless is deprecated/) + node.set_unless[:snoopy][:is_a_puppy] = false + expect(node[:snoopy][:is_a_puppy]).to eq(false) + end end describe "default attributes" do @@ -329,10 +352,41 @@ describe Chef::Node do expect(node[:snoopy][:is_a_puppy]).to eq(true) end + it "does not exhibit chef/chef/issues/5005 bug" do + node.env_default["a"]["r1"]["g"]["u"] = "u1" + node.default_unless["a"]["r1"]["g"]["r"] = "r" + expect(node["a"]["r1"]["g"]["u"]).to eql("u1") + end + it "auto-vivifies attributes created via method syntax" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false node.default.fuu.bahrr.baz = "qux" expect(node.fuu.bahrr.baz).to eq("qux") end + + it "default_unless correctly resets the deep merge cache" do + node.normal["tags"] = [] # this sets our top-level breadcrumb + node.default_unless["foo"]["bar"] = "NK-19V" + expect(node["foo"]["bar"]).to eql("NK-19V") + node.default_unless["foo"]["baz"] = "NK-33" + expect(node["foo"]["baz"]).to eql("NK-33") + end + + it "normal_unless correctly resets the deep merge cache" do + node.normal["tags"] = [] # this sets our top-level breadcrumb + node.normal_unless["foo"]["bar"] = "NK-19V" + expect(node["foo"]["bar"]).to eql("NK-19V") + node.normal_unless["foo"]["baz"] = "NK-33" + expect(node["foo"]["baz"]).to eql("NK-33") + end + + it "override_unless correctly resets the deep merge cache" do + node.normal["tags"] = [] # this sets our top-level breadcrumb + node.override_unless["foo"]["bar"] = "NK-19V" + expect(node["foo"]["bar"]).to eql("NK-19V") + node.override_unless["foo"]["baz"] = "NK-33" + expect(node["foo"]["baz"]).to eql("NK-33") + end end describe "override attributes" do @@ -368,6 +422,7 @@ describe Chef::Node do end it "auto-vivifies attributes created via method syntax" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false node.override.fuu.bahrr.baz = "qux" expect(node.fuu.bahrr.baz).to eq("qux") end @@ -453,8 +508,9 @@ describe Chef::Node do expect( node["mysql"]["server"][0]["port"] ).to be_nil end - it "does not have a horrible error message when mistaking arrays for hashes" do - expect { node.rm("mysql", "server", "port") }.to raise_error(TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)") + it "when mistaking arrays for hashes, it considers the value removed and does nothing" do + node.rm("mysql", "server", "port") + expect(node["mysql"]["server"][0]["port"]).to eql(3456) end end end @@ -706,6 +762,7 @@ describe Chef::Node do # describe "deep merge attribute cache edge conditions" do it "does not error with complicated attribute substitution" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false node.default["chef_attribute_hell"]["attr1"] = "attribute1" node.default["chef_attribute_hell"]["attr2"] = "#{node.chef_attribute_hell.attr1}/attr2" expect { node.default["chef_attribute_hell"]["attr3"] = "#{node.chef_attribute_hell.attr2}/attr3" }.not_to raise_error @@ -720,6 +777,7 @@ describe Chef::Node do end it "method interpolation syntax also works" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false node.default["passenger"]["version"] = "4.0.57" node.default["passenger"]["root_path"] = "passenger-#{node['passenger']['version']}" node.default["passenger"]["root_path_2"] = "passenger-#{node.passenger['version']}" @@ -729,6 +787,7 @@ describe Chef::Node do end it "should raise an ArgumentError if you ask for an attribute that doesn't exist via method_missing" do + Chef::Config[:treat_deprecation_warnings_as_errors] = false expect { node.sunshine }.to raise_error(NoMethodError) end @@ -744,6 +803,51 @@ describe Chef::Node do expect(seen_attributes["sunshine"]).to eq("is bright") expect(seen_attributes["canada"]).to eq("is a nice place") end + + describe "functional attribute API" do + # deeper functional testing of this API is in the VividMash spec tests + it "should have an exist? function" do + node.default["foo"]["bar"] = "baz" + expect(node.exist?("foo", "bar")).to be true + expect(node.exist?("bar", "foo")).to be false + end + + it "should have a read function" do + node.override["foo"]["bar"] = "baz" + expect(node.read("foo", "bar")).to eql("baz") + expect(node.read("bar", "foo")).to eql(nil) + end + + it "should have a read! function" do + node.override["foo"]["bar"] = "baz" + expect(node.read!("foo", "bar")).to eql("baz") + expect { node.read!("bar", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute) + end + + it "delegates write(:level) to node.level.write()" do + node.write(:default, "foo", "bar", "baz") + expect(node.default["foo"]["bar"]).to eql("baz") + end + + it "delegates write!(:level) to node.level.write!()" do + node.write!(:default, "foo", "bar", "baz") + expect(node.default["foo"]["bar"]).to eql("baz") + node.default["bar"] = true + expect { node.write!(:default, "bar", "foo", "baz") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch) + end + + it "delegates unlink(:level) to node.level.unlink()" do + node.default["foo"]["bar"] = "baz" + expect(node.unlink(:default, "foo", "bar")).to eql("baz") + expect(node.unlink(:default, "bar", "foo")).to eql(nil) + end + + it "delegates unlink!(:level) to node.level.unlink!()" do + node.default["foo"]["bar"] = "baz" + expect(node.unlink!(:default, "foo", "bar")).to eql("baz") + expect { node.unlink!(:default, "bar", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute) + end + end end describe "consuming json" do @@ -789,8 +893,8 @@ describe Chef::Node do it "should add json attributes to the node" do node.consume_external_attrs(@ohai_data, { "one" => "two", "three" => "four" }) - expect(node.one).to eql("two") - expect(node.three).to eql("four") + expect(node["one"]).to eql("two") + expect(node["three"]).to eql("four") end it "should set the tags attribute to an empty array if it is not already defined" do @@ -824,17 +928,17 @@ describe Chef::Node do it "deep merges attributes instead of overwriting them" do node.consume_external_attrs(@ohai_data, "one" => { "two" => { "three" => "four" } }) - expect(node.one.to_hash).to eq({ "two" => { "three" => "four" } }) + expect(node["one"].to_hash).to eq({ "two" => { "three" => "four" } }) node.consume_external_attrs(@ohai_data, "one" => { "abc" => "123" }) node.consume_external_attrs(@ohai_data, "one" => { "two" => { "foo" => "bar" } }) - expect(node.one.to_hash).to eq({ "two" => { "three" => "four", "foo" => "bar" }, "abc" => "123" }) + expect(node["one"].to_hash).to eq({ "two" => { "three" => "four", "foo" => "bar" }, "abc" => "123" }) end it "gives attributes from JSON priority when deep merging" do node.consume_external_attrs(@ohai_data, "one" => { "two" => { "three" => "four" } }) - expect(node.one.to_hash).to eq({ "two" => { "three" => "four" } }) + expect(node["one"].to_hash).to eq({ "two" => { "three" => "four" } }) node.consume_external_attrs(@ohai_data, "one" => { "two" => { "three" => "forty-two" } }) - expect(node.one.to_hash).to eq({ "two" => { "three" => "forty-two" } }) + expect(node["one"].to_hash).to eq({ "two" => { "three" => "forty-two" } }) end end @@ -1036,10 +1140,10 @@ describe Chef::Node do end it "sets attributes from the files" do - expect(node.ldap_server).to eql("ops1prod") - expect(node.ldap_basedn).to eql("dc=hjksolutions,dc=com") - expect(node.ldap_replication_password).to eql("forsure") - expect(node.smokey).to eql("robinson") + expect(node["ldap_server"]).to eql("ops1prod") + expect(node["ldap_basedn"]).to eql("dc=hjksolutions,dc=com") + expect(node["ldap_replication_password"]).to eql("forsure") + expect(node["smokey"]).to eql("robinson") end it "gives a sensible error when attempting to load a missing attributes file" do @@ -1083,8 +1187,8 @@ describe Chef::Node do it "should load a node from a ruby file" do node.from_file(File.expand_path(File.join(CHEF_SPEC_DATA, "nodes", "test.rb"))) expect(node.name).to eql("test.example.com-short") - expect(node.sunshine).to eql("in") - expect(node.something).to eql("else") + expect(node["sunshine"]).to eql("in") + expect(node["something"]).to eql("else") expect(node.run_list).to eq(["operations-master", "operations-monitoring"]) end @@ -1562,4 +1666,19 @@ describe Chef::Node do end end + describe "method_missing handling" do + it "should have an #empty? method via Chef::Node::Attribute" do + node.default["foo"] = "bar" + expect(node.empty?).to be false + end + + it "it should correctly implement #respond_to?" do + expect(node.respond_to?(:empty?)).to be true + end + + it "it should correctly retrieve the method with #method" do + expect(node.method(:empty?)).to be_kind_of(Method) + end + end + end diff --git a/spec/unit/provider/cron_spec.rb b/spec/unit/provider/cron_spec.rb index 010b1b09f3..9e849743e7 100644 --- a/spec/unit/provider/cron_spec.rb +++ b/spec/unit/provider/cron_spec.rb @@ -462,10 +462,10 @@ CRONTAB @new_resource.environment "TEST" => "LOL" expect(@provider).to receive(:write_crontab).with(<<-ENDCRON) # Chef Name: cronhole some stuff -MAILTO=foo@example.com -PATH=/usr/bin:/my/custom/path -SHELL=/bin/foosh -HOME=/home/foo +MAILTO="foo@example.com" +PATH="/usr/bin:/my/custom/path" +SHELL="/bin/foosh" +HOME="/home/foo" TEST=LOL 30 * * * * /bin/true ENDCRON @@ -524,10 +524,10 @@ TEST=LOL # Another comment # Chef Name: cronhole some stuff -MAILTO=foo@example.com -PATH=/usr/bin:/my/custom/path -SHELL=/bin/foosh -HOME=/home/foo +MAILTO="foo@example.com" +PATH="/usr/bin:/my/custom/path" +SHELL="/bin/foosh" +HOME="/home/foo" TEST=LOL 30 * * * * /bin/true ENDCRON @@ -585,10 +585,10 @@ TEST=LOL 0 2 * * * /some/other/command # Chef Name: cronhole some stuff -MAILTO=foo@example.com -PATH=/usr/bin:/my/custom/path -SHELL=/bin/foosh -HOME=/home/foo +MAILTO="foo@example.com" +PATH="/usr/bin:/my/custom/path" +SHELL="/bin/foosh" +HOME="/home/foo" TEST=LOL 30 * * * * /bin/true # Chef Name: something else @@ -679,10 +679,10 @@ HOME=/home/foo 0 2 * * * /some/other/command # Chef Name: cronhole some stuff -MAILTO=foo@example.com -PATH=/usr/bin:/my/custom/path -SHELL=/bin/foosh -HOME=/home/foo +MAILTO="foo@example.com" +PATH="/usr/bin:/my/custom/path" +SHELL="/bin/foosh" +HOME="/home/foo" 30 * * * * /bin/true # Chef Name: something else diff --git a/spec/unit/provider/package/aix_spec.rb b/spec/unit/provider/package/aix_spec.rb index 6940874a43..2b574bb7df 100644 --- a/spec/unit/provider/package/aix_spec.rb +++ b/spec/unit/provider/package/aix_spec.rb @@ -73,6 +73,19 @@ describe Chef::Provider::Package::Aix do expect(@new_resource.version).to eq("3.3.12.0") end + it "should warn if the package is not a fileset" do + info = "samba.base:samba.base.samples:3.3.12.0::COMMITTED:I:Samba for AIX: + /etc/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:" + status = double("Status", :stdout => info, :exitstatus => 0) + expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status) + expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status) + expect(Chef::Log).to receive(:warn).once.with(%r{bff package by product name}) + @provider.load_current_resource + + expect(@provider.current_resource.package_name).to eq("samba.base") + expect(@new_resource.version).to eq("3.3.12.0") + end + it "should return the current version installed if found by lslpp" do status = double("Status", :stdout => @bffinfo, :exitstatus => 0) @stdout = StringIO.new(@bffinfo) diff --git a/spec/unit/provider/package/windows/exe_spec.rb b/spec/unit/provider/package/windows/exe_spec.rb index 23a54601c2..b514f80a35 100644 --- a/spec/unit/provider/package/windows/exe_spec.rb +++ b/spec/unit/provider/package/windows/exe_spec.rb @@ -110,23 +110,28 @@ describe Chef::Provider::Package::Windows::Exe do end describe "remove_package" do - context "no version given and one package installed" do - it "removes installed package" do - expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir\" uninst_file \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash)) + before do + allow(::File).to receive(:exist?).and_return(false) + end + + context "no version given and one package installed with unquoted uninstall string" do + it "removes installed package and quotes uninstall string" do + allow(::File).to receive(:exist?).with("uninst_dir/uninst_file").and_return(true) + expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir\/uninst_file\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash)) provider.remove_package end end - context "several packages installed" do + context "several packages installed with quoted uninstall strings" do let(:uninstall_hash) do [ { "DisplayVersion" => "v1", - "UninstallString" => File.join("uninst_dir1", "uninst_file1"), + "UninstallString" => "\"#{File.join("uninst_dir1", "uninst_file1")}\"", }, { "DisplayVersion" => "v2", - "UninstallString" => File.join("uninst_dir2", "uninst_file2"), + "UninstallString" => "\"#{File.join("uninst_dir2", "uninst_file2")}\"", }, ] end @@ -134,15 +139,15 @@ describe Chef::Provider::Package::Windows::Exe do context "version given and installed" do it "removes given version" do new_resource.version("v2") - expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash)) + expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir2\/uninst_file2\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash)) provider.remove_package end end context "no version given" do it "removes both versions" do - expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir1\" uninst_file1 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash)) - expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash)) + expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir1\/uninst_file1\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash)) + expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir2\/uninst_file2\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash)) provider.remove_package end end diff --git a/spec/unit/provider/package/zypper_spec.rb b/spec/unit/provider/package/zypper_spec.rb index df0a1da9a2..8838c26b71 100644 --- a/spec/unit/provider/package/zypper_spec.rb +++ b/spec/unit/provider/package/zypper_spec.rb @@ -72,7 +72,7 @@ describe Chef::Provider::Package::Zypper do provider.load_current_resource end - it "should set the installed version if zypper info has one" do + it "should set the installed version if zypper info has one (zypper version < 1.13.0)" do status = double(:stdout => "Version: 1.0\nInstalled: Yes\n", :exitstatus => 0) allow(provider).to receive(:shell_out!).and_return(status) @@ -80,7 +80,15 @@ describe Chef::Provider::Package::Zypper do provider.load_current_resource end - it "should set the candidate version if zypper info has one" do + it "should set the installed version if zypper info has one (zypper version >= 1.13.0)" do + status = double(:stdout => "Version : 1.0 \nInstalled : Yes \n", :exitstatus => 0) + + allow(provider).to receive(:shell_out!).and_return(status) + expect(current_resource).to receive(:version).with(["1.0"]).and_return(true) + provider.load_current_resource + end + + it "should set the candidate version if zypper info has one (zypper version < 1.13.0)" do status = double(:stdout => "Version: 1.0\nInstalled: No\nStatus: out-of-date (version 0.9 installed)", :exitstatus => 0) allow(provider).to receive(:shell_out!).and_return(status) @@ -88,6 +96,14 @@ describe Chef::Provider::Package::Zypper do expect(provider.candidate_version).to eql(["1.0"]) end + it "should set the candidate version if zypper info has one (zypper version >= 1.13.0)" do + status = double(:stdout => "Version : 1.0 \nInstalled : No \nStatus : out-of-date (version 0.9 installed)", :exitstatus => 0) + + allow(provider).to receive(:shell_out!).and_return(status) + provider.load_current_resource + expect(provider.candidate_version).to eql(["1.0"]) + end + it "should return the current resouce" do expect(provider.load_current_resource).to eql(current_resource) end diff --git a/spec/unit/provider/powershell_script_spec.rb b/spec/unit/provider/powershell_script_spec.rb index 5b5c783986..96869ff31c 100644 --- a/spec/unit/provider/powershell_script_spec.rb +++ b/spec/unit/provider/powershell_script_spec.rb @@ -43,8 +43,8 @@ describe Chef::Provider::PowershellScript, "action_run" do allow(Chef::Platform).to receive(:windows_nano_server?).and_return(true) allow(provider).to receive(:is_forced_32bit).and_return(false) os_info_double = double("os_info") - allow(provider.run_context.node.kernel).to receive(:os_info).and_return(os_info_double) - allow(os_info_double).to receive(:system_directory).and_return("C:\\Windows\\system32") + allow(provider.run_context.node["kernel"]).to receive(:[]).with("os_info").and_return(os_info_double) + allow(os_info_double).to receive(:[]).with("system_directory").and_return("C:\\Windows\\system32") end it "sets the -Command flag as the last flag" do @@ -58,8 +58,8 @@ describe Chef::Provider::PowershellScript, "action_run" do allow(Chef::Platform).to receive(:windows_nano_server?).and_return(false) allow(provider).to receive(:is_forced_32bit).and_return(false) os_info_double = double("os_info") - allow(provider.run_context.node.kernel).to receive(:os_info).and_return(os_info_double) - allow(os_info_double).to receive(:system_directory).and_return("C:\\Windows\\system32") + allow(provider.run_context.node["kernel"]).to receive(:[]).with("os_info").and_return(os_info_double) + allow(os_info_double).to receive(:[]).with("system_directory").and_return("C:\\Windows\\system32") end it "sets the -File flag as the last flag" do diff --git a/spec/unit/provider/remote_directory_spec.rb b/spec/unit/provider/remote_directory_spec.rb index 710d6613fc..b6fd4cfc8e 100644 --- a/spec/unit/provider/remote_directory_spec.rb +++ b/spec/unit/provider/remote_directory_spec.rb @@ -99,6 +99,21 @@ describe Chef::Provider::RemoteDirectory do expect(cookbook_file.owner).to eq("toor") expect(cookbook_file.backup).to eq(23) end + + it "respects sensitive flag" do + @resource.cookbook "gondola_rides" + @resource.sensitive true + cookbook_file = @provider.send(:cookbook_file_resource, + "/target/destination/path.txt", + "relative/source/path.txt") + expect(cookbook_file.sensitive).to eq(true) + + @resource.sensitive false + cookbook_file = @provider.send(:cookbook_file_resource, + "/target/destination/path.txt", + "relative/source/path.txt") + expect(cookbook_file.sensitive).to eq(false) + end end describe "when creating the remote directory" do diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb index e5dbd42f70..3c30f96b20 100644 --- a/spec/unit/recipe_spec.rb +++ b/spec/unit/recipe_spec.rb @@ -195,9 +195,7 @@ describe Chef::Recipe do describe "when cloning resources" do def expect_warning - expect(Chef::Log).to receive(:warn).with(/3694/) - expect(Chef::Log).to receive(:warn).with(/Previous/) - expect(Chef::Log).to receive(:warn).with(/Current/) + expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/) end it "should emit a 3694 warning when attributes change" do @@ -244,7 +242,7 @@ describe Chef::Recipe do it "should not emit a 3694 warning for completely trivial resource cloning" do recipe.zen_master "klopp" - expect(Chef::Log).to_not receive(:warn) + expect(Chef).to_not receive(:log_deprecation) recipe.zen_master "klopp" end @@ -252,7 +250,7 @@ describe Chef::Recipe do recipe.zen_master "klopp" do action :nothing end - expect(Chef::Log).to_not receive(:warn) + expect(Chef).to_not receive(:log_deprecation) recipe.zen_master "klopp" do action :score end @@ -262,12 +260,33 @@ describe Chef::Recipe do recipe.zen_master "klopp" do action :score end - expect(Chef::Log).to_not receive(:warn) + expect(Chef).to_not receive(:log_deprecation) recipe.zen_master "klopp" do action :nothing end end + class Coerced < Chef::Resource + resource_name :coerced + provides :coerced + default_action :whatever + property :package_name, [String, Array], coerce: proc { |x| [x].flatten }, name_property: true + def after_created + Array(action).each do |action| + run_action(action) + end + end + action :whatever do + package_name # unlazy the package_name + end + end + + it "does not emit 3694 when the name_property is unlazied by running it at compile_time" do + recipe.coerced "string" + expect(Chef).to_not receive(:log_deprecation) + recipe.coerced "string" + end + it "validating resources via build_resource" do expect {recipe.build_resource(:remote_file, "klopp") do source Chef::DelayedEvaluator.new { "http://chef.io" } @@ -299,6 +318,7 @@ describe Chef::Recipe do end it "will insert another resource if create_if_missing is not set (cloned resource as of Chef-12)" do + expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/) zm_resource recipe.declare_resource(:zen_master, "klopp") expect(run_context.resource_collection.count).to eql(2) @@ -421,15 +441,18 @@ describe Chef::Recipe do end it "copies attributes from the first resource" do + expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/) expect(duplicated_resource.something).to eq("bvb09") end it "does not copy the action from the first resource" do + expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/) expect(original_resource.action).to eq([:score]) expect(duplicated_resource.action).to eq([:nothing]) end it "does not copy the source location of the first resource" do + expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/) # sanity check source location: expect(original_resource.source_line).to include(__FILE__) expect(duplicated_resource.source_line).to include(__FILE__) @@ -438,10 +461,12 @@ describe Chef::Recipe do end it "sets the cookbook name on the cloned resource to that resource's cookbook" do + expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/) expect(duplicated_resource.cookbook_name).to eq("second_cb") end it "sets the recipe name on the cloned resource to that resoure's recipe" do + expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/) expect(duplicated_resource.recipe_name).to eq("second_recipe") end diff --git a/spec/unit/run_context_spec.rb b/spec/unit/run_context_spec.rb index 7b2a27e9f6..234cd3c9e1 100644 --- a/spec/unit/run_context_spec.rb +++ b/spec/unit/run_context_spec.rb @@ -153,8 +153,8 @@ describe Chef::RunContext do let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) } let(:node) { node = Chef::Node.new - node.set[:platform] = "ubuntu" - node.set[:platform_version] = "13.04" + node.normal[:platform] = "ubuntu" + node.normal[:platform_version] = "13.04" node.name("testing") node } diff --git a/spec/unit/shell/shell_session_spec.rb b/spec/unit/shell/shell_session_spec.rb index 22f3dc6f0c..259e6096a4 100644 --- a/spec/unit/shell/shell_session_spec.rb +++ b/spec/unit/shell/shell_session_spec.rb @@ -158,7 +158,7 @@ describe Shell::SoloSession do it "keeps json attribs and passes them to the node for consumption" do @session.node_attributes = { "besnard_lakes" => "are_the_dark_horse" } - expect(@session.node.besnard_lakes).to eq("are_the_dark_horse") + expect(@session.node["besnard_lakes"]).to eq("are_the_dark_horse") #pending "1) keep attribs in an ivar 2) pass them to the node 3) feed them to the node on reset" end diff --git a/tasks/dependencies.rb b/tasks/dependencies.rb index eb4bb1f44f..0b216f8e52 100644 --- a/tasks/dependencies.rb +++ b/tasks/dependencies.rb @@ -85,13 +85,11 @@ namespace :dependencies do other_platforms: false, leave_frozen: false gemfile_lock_task :update_kitchen_tests_gemfile_lock, dirs: %w{ kitchen-tests - kitchen-tests/test/integration/webapp/serverspec } berksfile_lock_task :update_kitchen_tests_berksfile_lock, dirs: %w{ kitchen-tests kitchen-tests/cookbooks/audit_test } - # kitchen-tests/cookbooks/webapp isn't solving right now .... desc "Update omnibus overrides, including versions in version_policy.rb and latest version of gems: #{OMNIBUS_RUBYGEMS_AT_LATEST_VERSION.keys}." task :update_omnibus_overrides do |t, rake_args| diff --git a/version_policy.rb b/version_policy.rb index 8d450010d3..b8bead7bef 100644 --- a/version_policy.rb +++ b/version_policy.rb @@ -26,8 +26,8 @@ OMNIBUS_OVERRIDES = { ## according to comment in omnibus-sw, the very latest versions don't work on solaris # https://github.com/chef/omnibus-software/blob/aefb7e79d29ca746c3f843673ef5e317fa3cba54/config/software/libtool.rb#L23 "libtool" => "2.4.2", - "libxml2" => "2.9.3", - "libxslt" => "1.1.28", + "libxml2" => "2.9.4", + "libxslt" => "1.1.29", "libyaml" => "0.1.6", "makedepend" => "1.0.5", "ncurses" => "5.9", |