summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordanielsdeleo <dan@getchef.com>2015-04-23 11:44:24 -0700
committerdanielsdeleo <dan@getchef.com>2015-05-20 15:13:56 -0700
commite6062caaefe82ff351690af15e882f02efc0f91b (patch)
tree850f5ed39e9bd7e33303bf907c4ea7389a6a33d7
parentb6f9e5feff3b97576534d70dc2873c4fd62d28e4 (diff)
downloadchef-e6062caaefe82ff351690af15e882f02efc0f91b.tar.gz
Move Chef::Config into a subproject
-rw-r--r--Gemfile2
-rw-r--r--chef-config/.gitignore9
-rw-r--r--chef-config/.rspec2
-rw-r--r--chef-config/.travis.yml31
-rw-r--r--chef-config/Gemfile4
-rw-r--r--chef-config/LICENSE201
-rw-r--r--chef-config/README.md4
-rw-r--r--chef-config/Rakefile2
-rw-r--r--chef-config/chef-config.gemspec32
-rw-r--r--chef-config/lib/chef-config.rb20
-rw-r--r--chef-config/lib/chef-config/config.rb744
-rw-r--r--chef-config/lib/chef-config/exceptions.rb26
-rw-r--r--chef-config/lib/chef-config/logger.rb62
-rw-r--r--chef-config/lib/chef-config/path_helper.rb233
-rw-r--r--chef-config/lib/chef-config/version.rb4
-rw-r--r--chef-config/lib/chef-config/windows.rb29
-rw-r--r--chef-config/spec/spec_helper.rb75
-rw-r--r--chef-config/spec/unit/config_spec.rb581
-rw-r--r--chef-config/spec/unit/path_helper_spec.rb291
-rw-r--r--chef.gemspec3
-rw-r--r--lib/chef/config.rb736
-rw-r--r--lib/chef/provider/file.rb1
22 files changed, 2357 insertions, 735 deletions
diff --git a/Gemfile b/Gemfile
index 8cb8f22e32..d3da2919da 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,6 +3,8 @@ gemspec :name => "chef"
gem "activesupport", "< 4.0.0", :group => :compat_testing, :platform => "ruby"
+gem 'chef-config', path: "chef-config"
+
group(:docgen) do
gem "yard"
end
diff --git a/chef-config/.gitignore b/chef-config/.gitignore
new file mode 100644
index 0000000000..0cb6eeb067
--- /dev/null
+++ b/chef-config/.gitignore
@@ -0,0 +1,9 @@
+/.bundle/
+/.yardoc
+/Gemfile.lock
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
diff --git a/chef-config/.rspec b/chef-config/.rspec
new file mode 100644
index 0000000000..eb3ef03653
--- /dev/null
+++ b/chef-config/.rspec
@@ -0,0 +1,2 @@
+--color
+-fd
diff --git a/chef-config/.travis.yml b/chef-config/.travis.yml
new file mode 100644
index 0000000000..927580f35f
--- /dev/null
+++ b/chef-config/.travis.yml
@@ -0,0 +1,31 @@
+language: ruby
+
+sudo: false
+# Early warning system to catch if Rubygems breaks something
+before_install:
+ gem update --system
+
+branches:
+ only:
+ - master
+
+matrix:
+ include:
+ - rvm: 2.0
+ - rvm: 2.1
+
+notifications:
+ on_change: true
+ on_failure: true
+ on_success: change
+ on_pull_requests: false
+ irc:
+ channels:
+ - chat.freenode.net#chef-hacking
+ hipchat:
+ rooms:
+ # Build Statuses
+ - secure: G8MNo94L8bmWWwkH2/ViB2QaZnZHZscYM/mEjDbOGd15sqrruwckeARyBoUcRI7P1C6AFmS4IKCNVXa6KzX4Pbh51gQWM92zRpRTZpplwtXz53/1l8ajLFLLMLvEMTlBFAANUKEUFAQPY4dMa14V3Qc5oijfIncN61k4nZNTKpY=
+ - rvm: 2.2
+ # Open Source
+ - secure: hmcex4PpG5dn8WvjndONO4xCUKOC5kPU/bUEGRrfVbe2YKJE7t0XXbNDC96W/xBgzgnJzvf1Er0zJKDrNf4qEDEWFoozdN00WLcqREgaLLS3Seto2FjR/BpBk5q+sCV0rwwEMms2P4Qk+VSnDCnm9EaeM55hOabqNuOrRzoZLBQ=
diff --git a/chef-config/Gemfile b/chef-config/Gemfile
new file mode 100644
index 0000000000..d39725ff87
--- /dev/null
+++ b/chef-config/Gemfile
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in chef-config.gemspec
+gemspec
diff --git a/chef-config/LICENSE b/chef-config/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/chef-config/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/chef-config/README.md b/chef-config/README.md
new file mode 100644
index 0000000000..c36527282e
--- /dev/null
+++ b/chef-config/README.md
@@ -0,0 +1,4 @@
+# ChefConfig
+
+This repo is experimental. Use at your own risk.
+
diff --git a/chef-config/Rakefile b/chef-config/Rakefile
new file mode 100644
index 0000000000..809eb5616a
--- /dev/null
+++ b/chef-config/Rakefile
@@ -0,0 +1,2 @@
+require "bundler/gem_tasks"
+
diff --git a/chef-config/chef-config.gemspec b/chef-config/chef-config.gemspec
new file mode 100644
index 0000000000..74789a7328
--- /dev/null
+++ b/chef-config/chef-config.gemspec
@@ -0,0 +1,32 @@
+# coding: utf-8
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'chef-config/version'
+
+Gem::Specification.new do |spec|
+ spec.name = "chef-config"
+ spec.version = ChefConfig::VERSION
+ spec.authors = ["Adam Jacob"]
+ spec.email = ["adam@chef.io"]
+
+ spec.summary = %q{Chef's default configuration and config loading}
+ spec.homepage = "TODO: Put your gem's website or public repo URL here."
+ spec.license = "Apache-2.0"
+
+ spec.require_paths = ["lib"]
+
+ spec.add_dependency "mixlib-shellout", "~> 2.0"
+ spec.add_dependency "mixlib-config", "~> 2.0"
+
+ spec.add_development_dependency "rake", "~> 10.0"
+
+ %w(rspec-core rspec-expectations rspec-mocks).each do |rspec|
+ spec.add_development_dependency(rspec, "~> 3.2")
+ end
+
+ spec.files = %w(Rakefile LICENSE README.md CONTRIBUTING.md) +
+ Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
+
+ spec.bindir = "bin"
+ spec.executables = []
+end
diff --git a/chef-config/lib/chef-config.rb b/chef-config/lib/chef-config.rb
new file mode 100644
index 0000000000..1f593c868f
--- /dev/null
+++ b/chef-config/lib/chef-config.rb
@@ -0,0 +1,20 @@
+#
+# Copyright:: Copyright (c) 2015 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.
+#
+
+module ChefConfig
+
+end
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
new file mode 100644
index 0000000000..549fac5cbf
--- /dev/null
+++ b/chef-config/lib/chef-config/config.rb
@@ -0,0 +1,744 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: AJ Christensen (<aj@opscode.com>)
+# Author:: Mark Mzyk (<mmzyk@opscode.com>)
+# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
+# Copyright:: Copyright (c) 2008 Opscode, 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 'mixlib/config'
+require 'pathname'
+
+require 'chef-config/logger'
+require 'chef-config/windows'
+require 'chef-config/path_helper'
+require 'mixlib/shellout'
+
+module ChefConfig
+
+ class Config
+
+ extend Mixlib::Config
+
+ # Evaluates the given string as config.
+ #
+ # +filename+ is used for context in stacktraces, but doesn't need to be the name of an actual file.
+ def self.from_string(string, filename)
+ self.instance_eval(string, filename, 1)
+ end
+
+ def self.inspect
+ configuration.inspect
+ end
+
+ def self.platform_specific_path(path)
+ path = PathHelper.cleanpath(path)
+ if ChefConfig.windows?
+ # turns \etc\chef\client.rb and \var\chef\client.rb into C:/chef/client.rb
+ if env['SYSTEMDRIVE'] && path[0] == '\\' && path.split('\\')[2] == 'chef'
+ path = PathHelper.join(env['SYSTEMDRIVE'], path.split('\\', 3)[2])
+ end
+ end
+ path
+ end
+
+ def self.add_formatter(name, file_path=nil)
+ formatters << [name, file_path]
+ end
+
+ def self.add_event_logger(logger)
+ event_handlers << logger
+ end
+
+ # Config file to load (client.rb, knife.rb, etc. defaults set differently in knife, chef-client, etc.)
+ configurable(:config_file)
+
+ default(:config_dir) do
+ if config_file
+ PathHelper.dirname(config_file)
+ else
+ PathHelper.join(user_home, ".chef", "")
+ end
+ end
+
+ default :formatters, []
+
+ # Override the config dispatch to set the value of multiple server options simultaneously
+ #
+ # === Parameters
+ # url<String>:: String to be set for all of the chef-server-api URL's
+ #
+ configurable(:chef_server_url).writes_value { |url| url.to_s.strip }
+
+ # When you are using ActiveSupport, they monkey-patch 'daemonize' into Kernel.
+ # So while this is basically identical to what method_missing would do, we pull
+ # it up here and get a real method written so that things get dispatched
+ # properly.
+ configurable(:daemonize).writes_value { |v| v }
+
+ # The root where all local chef object data is stored. cookbooks, data bags,
+ # environments are all assumed to be in separate directories under this.
+ # chef-solo uses these directories for input data. knife commands
+ # that upload or download files (such as knife upload, knife role from file,
+ # etc.) work.
+ default :chef_repo_path do
+ if self.configuration[:cookbook_path]
+ if self.configuration[:cookbook_path].kind_of?(String)
+ File.expand_path('..', self.configuration[:cookbook_path])
+ else
+ self.configuration[:cookbook_path].map do |path|
+ File.expand_path('..', path)
+ end
+ end
+ else
+ cache_path
+ end
+ end
+
+ def self.find_chef_repo_path(cwd)
+ # In local mode, we auto-discover the repo root by looking for a path with "cookbooks" under it.
+ # This allows us to run config-free.
+ path = cwd
+ until File.directory?(PathHelper.join(path, "cookbooks"))
+ 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
+ end
+ path = new_path
+ end
+ ChefConfig.logger.info("Auto-discovered chef repository at #{path}")
+ path
+ end
+
+ def self.derive_path_from_chef_repo_path(child_path)
+ if chef_repo_path.kind_of?(String)
+ PathHelper.join(chef_repo_path, child_path)
+ else
+ chef_repo_path.map { |path| PathHelper.join(path, child_path)}
+ end
+ end
+
+ # Location of acls on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/acls.
+ # Only applies to Enterprise Chef commands.
+ default(:acl_path) { derive_path_from_chef_repo_path('acls') }
+
+ # Location of clients on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/acls.
+ default(:client_path) { derive_path_from_chef_repo_path('clients') }
+
+ # Location of cookbooks on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/cookbooks. If chef_repo_path
+ # is not specified, this is set to [/var/chef/cookbooks, /var/chef/site-cookbooks]).
+ default(:cookbook_path) do
+ if self.configuration[:chef_repo_path]
+ derive_path_from_chef_repo_path('cookbooks')
+ else
+ Array(derive_path_from_chef_repo_path('cookbooks')).flatten +
+ Array(derive_path_from_chef_repo_path('site-cookbooks')).flatten
+ end
+ end
+
+ # Location of containers on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/containers.
+ # Only applies to Enterprise Chef commands.
+ default(:container_path) { derive_path_from_chef_repo_path('containers') }
+
+ # Location of data bags on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/data_bags.
+ default(:data_bag_path) { derive_path_from_chef_repo_path('data_bags') }
+
+ # Location of environments on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/environments.
+ default(:environment_path) { derive_path_from_chef_repo_path('environments') }
+
+ # Location of groups on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/groups.
+ # Only applies to Enterprise Chef commands.
+ default(:group_path) { derive_path_from_chef_repo_path('groups') }
+
+ # Location of nodes on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/nodes.
+ default(:node_path) { derive_path_from_chef_repo_path('nodes') }
+
+ # Location of roles on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/roles.
+ default(:role_path) { derive_path_from_chef_repo_path('roles') }
+
+ # Location of users on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/users.
+ # Does not apply to Enterprise Chef commands.
+ default(:user_path) { derive_path_from_chef_repo_path('users') }
+
+ # Location of policies on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/policies.
+ default(:policy_path) { derive_path_from_chef_repo_path('policies') }
+
+ # Turn on "path sanity" by default. See also: http://wiki.opscode.com/display/chef/User+Environment+PATH+Sanity
+ default :enforce_path_sanity, true
+
+ # Formatted Chef Client output is a beta feature, disabled by default:
+ default :formatter, "null"
+
+ # The number of times the client should retry when registering with the server
+ default :client_registration_retries, 5
+
+ # An array of paths to search for knife exec scripts if they aren't in the current directory
+ default :script_path, []
+
+ # The root of all caches (checksums, cache and backup). If local mode is on,
+ # this is under the user's home directory.
+ default(:cache_path) do
+ if local_mode
+ PathHelper.join(config_dir, 'local-mode-cache')
+ else
+ primary_cache_root = platform_specific_path("/var")
+ primary_cache_path = platform_specific_path("/var/chef")
+ # Use /var/chef as the cache path only if that folder exists and we can read and write
+ # into it, or /var exists and we can read and write into it (we'll create /var/chef later).
+ # Otherwise, we'll create .chef under the user's home directory and use that as
+ # the cache path.
+ unless path_accessible?(primary_cache_path) || path_accessible?(primary_cache_root)
+ secondary_cache_path = PathHelper.join(user_home, '.chef')
+ ChefConfig.logger.info("Unable to access cache at #{primary_cache_path}. Switching cache to #{secondary_cache_path}")
+ secondary_cache_path
+ else
+ primary_cache_path
+ end
+ end
+ end
+
+ # Returns true only if the path exists and is readable and writeable for the user.
+ def self.path_accessible?(path)
+ File.exists?(path) && File.readable?(path) && File.writable?(path)
+ end
+
+ # Where cookbook files are stored on the server (by content checksum)
+ default(:checksum_path) { PathHelper.join(cache_path, "checksums") }
+
+ # Where chef's cache files should be stored
+ default(:file_cache_path) { PathHelper.join(cache_path, "cache") }
+
+ # Where backups of chef-managed files should go
+ default(:file_backup_path) { PathHelper.join(cache_path, "backup") }
+
+ # The chef-client (or solo) lockfile.
+ #
+ # If your `file_cache_path` resides on a NFS (or non-flock()-supporting
+ # fs), it's recommended to set this to something like
+ # '/tmp/chef-client-running.pid'
+ default(:lockfile) { PathHelper.join(file_cache_path, "chef-client-running.pid") }
+
+ ## Daemonization Settings ##
+ # What user should Chef run as?
+ default :user, nil
+ default :group, nil
+ default :umask, 0022
+
+ # Valid log_levels are:
+ # * :debug
+ # * :info
+ # * :warn
+ # * :fatal
+ # These work as you'd expect. There is also a special `:auto` setting.
+ # When set to :auto, Chef will auto adjust the log verbosity based on
+ # context. When a tty is available (usually because the user is running chef
+ # in a console), the log level is set to :warn, and output formatters are
+ # used as the primary mode of output. When a tty is not available, the
+ # logger is the primary mode of output, and the log level is set to :info
+ default :log_level, :auto
+
+ # Logging location as either an IO stream or string representing log file path
+ default :log_location, STDOUT
+
+ # Using `force_formatter` causes chef to default to formatter output when STDOUT is not a tty
+ default :force_formatter, false
+
+ # Using `force_logger` causes chef to default to logger output when STDOUT is a tty
+ default :force_logger, false
+
+ default :http_retry_count, 5
+ default :http_retry_delay, 5
+ default :interval, nil
+ default :once, nil
+ default :json_attribs, nil
+ # toggle info level log items that can create a lot of output
+ default :verbose_logging, true
+ default :node_name, nil
+ default :diff_disabled, false
+ default :diff_filesize_threshold, 10000000
+ default :diff_output_threshold, 1000000
+ default :local_mode, false
+
+ default :pid_file, nil
+
+ # Whether Chef Zero local mode should bind to a port. All internal requests
+ # will go through the socketless code path regardless, so the socket is
+ # only needed if other processes will connect to the local mode server.
+ #
+ # For compatibility this is set to true but it will be changed to false in
+ # the future.
+ default :listen, true
+
+ config_context :chef_zero do
+ config_strict_mode true
+ default(:enabled) { ChefConfig::Config.local_mode }
+ default :host, 'localhost'
+ default :port, 8889.upto(9999) # Will try ports from 8889-9999 until one works
+ end
+ default :chef_server_url, "https://localhost:443"
+
+ default(:chef_server_root) do
+ # if the chef_server_url is a path to an organization, aka
+ # 'some_url.../organizations/*' then remove the '/organization/*' by default
+ if self.configuration[:chef_server_url] =~ /\/organizations\/\S*$/
+ self.configuration[:chef_server_url].split('/')[0..-3].join('/')
+ elsif self.configuration[:chef_server_url] # default to whatever chef_server_url is
+ self.configuration[:chef_server_url]
+ else
+ "https://localhost:443"
+ end
+ end
+
+ default :rest_timeout, 300
+ default :yum_timeout, 900
+ default :yum_lock_timeout, 30
+ default :solo, false
+ default :splay, nil
+ default :why_run, false
+ default :color, false
+ default :client_fork, true
+ default :ez, false
+ default :enable_reporting, true
+ default :enable_reporting_url_fatals, false
+ # Possible values for :audit_mode
+ # :enabled, :disabled, :audit_only,
+ #
+ # TODO: 11 Dec 2014: Currently audit-mode is an experimental feature
+ # and is disabled by default. When users choose to enable audit-mode,
+ # a warning is issued in application/client#reconfigure.
+ # This can be removed when audit-mode is enabled by default.
+ default :audit_mode, :disabled
+
+ # Chef only needs ohai to run the hostname plugin for the most basic
+ # functionality. If the rest of the ohai plugins are not needed (like in
+ # most of our testing scenarios)
+ default :minimal_ohai, false
+
+ # Policyfile is an experimental feature where a node gets its run list and
+ # cookbook version set from a single document on the server instead of
+ # expanding the run list and having the server compute the cookbook version
+ # set based on environment constraints.
+ #
+ # Because this feature is experimental, it is not recommended for
+ # production use. Developent/release of this feature may not adhere to
+ # semver guidelines.
+ default :use_policyfile, false
+
+ # Set these to enable SSL authentication / mutual-authentication
+ # with the server
+
+ # Client side SSL cert/key for mutual auth
+ default :ssl_client_cert, nil
+ default :ssl_client_key, nil
+
+ # Whether or not to verify the SSL cert for all HTTPS requests. When set to
+ # :verify_peer (default), all HTTPS requests will be validated regardless of other
+ # SSL verification settings. When set to :verify_none no HTTPS requests will
+ # be validated.
+ default :ssl_verify_mode, :verify_peer
+
+ # Whether or not to verify the SSL cert for HTTPS requests to the Chef
+ # server API. If set to `true`, the server's cert will be validated
+ # regardless of the :ssl_verify_mode setting. This is set to `true` when
+ # running in local-mode.
+ # NOTE: This is a workaround until verify_peer is enabled by default.
+ default(:verify_api_cert) { ChefConfig::Config.local_mode }
+
+ # Path to the default CA bundle files.
+ default :ssl_ca_path, nil
+ default(:ssl_ca_file) do
+ if ChefConfig.windows? and embedded_path = embedded_dir
+ cacert_path = File.join(embedded_path, "ssl/certs/cacert.pem")
+ cacert_path if File.exist?(cacert_path)
+ else
+ nil
+ end
+ end
+
+ # A directory that contains additional SSL certificates to trust. Any
+ # certificates in this directory will be added to whatever CA bundle ruby
+ # is using. Use this to add self-signed certs for your Chef Server or local
+ # HTTP file servers.
+ default(:trusted_certs_dir) { PathHelper.join(config_dir, "trusted_certs") }
+
+ # Where should chef-solo download recipes from?
+ default :recipe_url, nil
+
+ # Sets the version of the signed header authentication protocol to use (see
+ # the 'mixlib-authorization' project for more detail). Currently, versions
+ # 1.0 and 1.1 are available; however, the chef-server must first be
+ # upgraded to support version 1.1 before clients can begin using it.
+ #
+ # Version 1.1 of the protocol is required when using a `node_name` greater
+ # than ~90 bytes (~90 ascii characters), so chef-client will automatically
+ # switch to using version 1.1 when `node_name` is too large for the 1.0
+ # protocol. If you intend to use large node names, ensure that your server
+ # supports version 1.1. Automatic detection of large node names means that
+ # users will generally not need to manually configure this.
+ #
+ # In the future, this configuration option may be replaced with an
+ # automatic negotiation scheme.
+ default :authentication_protocol_version, "1.0"
+
+ # This key will be used to sign requests to the Chef server. This location
+ # must be writable by Chef during initial setup when generating a client
+ # identity on the server.
+ #
+ # The chef-server will look up the public key for the client using the
+ # `node_name` of the client.
+ #
+ # If chef-zero is enabled, this defaults to nil (no authentication).
+ default(:client_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/client.pem") }
+
+ # When registering the client, should we allow the client key location to
+ # be a symlink? eg: /etc/chef/client.pem -> /etc/chef/prod-client.pem
+ # If the path of the key goes through a directory like /tmp this should
+ # never be set to true or its possibly an easily exploitable security hole.
+ default :follow_client_key_symlink, false
+
+ # This secret is used to decrypt encrypted data bag items.
+ default(:encrypted_data_bag_secret) do
+ if File.exist?(platform_specific_path("/etc/chef/encrypted_data_bag_secret"))
+ platform_specific_path("/etc/chef/encrypted_data_bag_secret")
+ else
+ nil
+ end
+ end
+
+ # As of Chef 11.0, version "1" is the default encrypted data bag item
+ # format. Version "2" is available which adds encrypt-then-mac protection.
+ # To maintain compatibility, versions other than 1 must be opt-in.
+ #
+ # Set this to `2` if you have chef-client 11.6.0+ in your infrastructure.
+ # Set this to `3` if you have chef-client 11.?.0+, ruby 2 and OpenSSL >= 1.0.1 in your infrastructure. (TODO)
+ default :data_bag_encrypt_version, 1
+
+ # When reading data bag items, any supported version is accepted. However,
+ # if all encrypted data bags have been generated with the version 2 format,
+ # it is recommended to disable support for earlier formats to improve
+ # security. For example, the version 2 format is identical to version 1
+ # except for the addition of an HMAC, so an attacker with MITM capability
+ # could downgrade an encrypted data bag to version 1 as part of an attack.
+ default :data_bag_decrypt_minimum_version, 0
+
+ # If there is no file in the location given by `client_key`, chef-client
+ # will temporarily use the "validator" identity to generate one. If the
+ # `client_key` is not present and the `validation_key` is also not present,
+ # chef-client will not be able to authenticate to the server.
+ #
+ # The `validation_key` is never used if the `client_key` exists.
+ #
+ # If chef-zero is enabled, this defaults to nil (no authentication).
+ default(:validation_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/validation.pem") }
+ default :validation_client_name, "chef-validator"
+
+ # When creating a new client via the validation_client account, Chef 11
+ # servers allow the client to generate a key pair locally and send the
+ # public key to the server. This is more secure and helps offload work from
+ # the server, enhancing scalability. If enabled and the remote server
+ # implements only the Chef 10 API, client registration will not work
+ # properly.
+ #
+ # The default value is `true`. Set to `false` to disable client-side key
+ # generation (server generates client keys).
+ default(:local_key_generation) { true }
+
+ # Zypper package provider gpg checks. Set to true to enable package
+ # gpg signature checking. This will be default in the
+ # future. Setting to false disables the warnings.
+ # Leaving this set to nil or false is a security hazard!
+ default :zypper_check_gpg, nil
+
+ # Report Handlers
+ default :report_handlers, []
+
+ # Event Handlers
+ default :event_handlers, []
+
+ default :disable_event_loggers, false
+ default :event_loggers do
+ evt_loggers = []
+ if ChefConfig::windows? and not ChefConfig::windows_server_2003?
+ evt_loggers << :win_evt
+ end
+ evt_loggers
+ end
+
+ # Exception Handlers
+ default :exception_handlers, []
+
+ # Start handlers
+ default :start_handlers, []
+
+ # Syntax Check Cache. Knife keeps track of files that is has already syntax
+ # checked by storing files in this directory. `syntax_check_cache_path` is
+ # the new (and preferred) configuration setting. If not set, knife will
+ # fall back to using cache_options[:path], which is deprecated but exists in
+ # many client configs generated by pre-Chef-11 bootstrappers.
+ default(:syntax_check_cache_path) { cache_options[:path] }
+
+ # Deprecated:
+ # Move this to the default value of syntax_cache_path when this is removed.
+ default(:cache_options) { { :path => PathHelper.join(config_dir, "syntaxcache") } }
+
+ # Whether errors should be raised for deprecation warnings. When set to
+ # `false` (the default setting), a warning is emitted but code using
+ # deprecated methods/features/etc. should work normally otherwise. When set
+ # to `true`, usage of deprecated methods/features will raise a
+ # `DeprecatedFeatureError`. This is used by Chef's tests to ensure that
+ # deprecated functionality is not used internally by Chef. End users
+ # should generally leave this at the default setting (especially in
+ # production), but it may be useful when testing cookbooks or other code if
+ # the user wishes to aggressively address deprecations.
+ default(:treat_deprecation_warnings_as_errors) do
+ # Using an environment variable allows this setting to be inherited in
+ # tests that spawn new processes.
+ ENV.key?("CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS")
+ end
+
+ # knife configuration data
+ config_context :knife do
+ default :ssh_port, nil
+ default :ssh_user, nil
+ default :ssh_attribute, nil
+ default :ssh_gateway, nil
+ default :bootstrap_version, nil
+ default :bootstrap_proxy, nil
+ default :bootstrap_template, nil
+ default :secret, nil
+ default :secret_file, nil
+ default :identity_file, nil
+ default :host_key_verify, nil
+ default :forward_agent, nil
+ default :sort_status_reverse, nil
+ default :hints, {}
+ end
+
+ def self.set_defaults_for_windows
+ # Those lists of regular expressions define what chef considers a
+ # valid user and group name
+ # From http://technet.microsoft.com/en-us/library/cc776019(WS.10).aspx
+ principal_valid_regex_part = '[^"\/\\\\\[\]\:;|=,+*?<>]+'
+ default :user_valid_regex, [ /^(#{principal_valid_regex_part}\\)?#{principal_valid_regex_part}$/ ]
+ default :group_valid_regex, [ /^(#{principal_valid_regex_part}\\)?#{principal_valid_regex_part}$/ ]
+
+ default :fatal_windows_admin_check, false
+ end
+
+ def self.set_defaults_for_nix
+ # Those lists of regular expressions define what chef considers a
+ # valid user and group name
+ #
+ # user/group cannot start with '-', '+' or '~'
+ # user/group cannot contain ':', ',' or non-space-whitespace or null byte
+ # everything else is allowed (UTF-8, spaces, etc) and we delegate to your O/S useradd program to barf or not
+ # copies: http://anonscm.debian.org/viewvc/pkg-shadow/debian/trunk/debian/patches/506_relaxed_usernames?view=markup
+ default :user_valid_regex, [ /^[^-+~:,\t\r\n\f\0]+[^:,\t\r\n\f\0]*$/ ]
+ default :group_valid_regex, [ /^[^-+~:,\t\r\n\f\0]+[^:,\t\r\n\f\0]*$/ ]
+ end
+
+ # Those lists of regular expressions define what chef considers a
+ # valid user and group name
+ if ChefConfig.windows?
+ set_defaults_for_windows
+ else
+ set_defaults_for_nix
+ end
+
+ # This provides a hook which rspec can stub so that we can avoid twiddling
+ # global state in tests.
+ def self.env
+ ENV
+ end
+
+ def self.windows_home_path
+ ChefConfig.logger.deprecation("Chef::Config.windows_home_path is now deprecated. Consider using Chef::Util::PathHelper.home instead.")
+ PathHelper.home
+ end
+
+ # returns a platform specific path to the user home dir if set, otherwise default to current directory.
+ default( :user_home ) { PathHelper.home || Dir.pwd }
+
+ # Enable file permission fixup for selinux. Fixup will be done
+ # only if selinux is enabled in the system.
+ default :enable_selinux_file_permission_fixup, true
+
+ # Use atomic updates (i.e. move operation) while updating contents
+ # of the files resources. When set to false copy operation is
+ # used to update files.
+ default :file_atomic_update, true
+
+ # There are 3 possible values for this configuration setting.
+ # true => file staging is done in the destination directory
+ # false => file staging is done via tempfiles under ENV['TMP']
+ # :auto => file staging will try using destination directory if possible and
+ # will fall back to ENV['TMP'] if destination directory is not usable.
+ default :file_staging_uses_destdir, :auto
+
+ # Exit if another run is in progress and the chef-client is unable to
+ # get the lock before time expires. If nil, no timeout is enforced. (Exits
+ # immediately if 0.)
+ default :run_lock_timeout, nil
+
+ # Number of worker threads for syncing cookbooks in parallel. Increasing
+ # this number can result in gateway errors from the server (namely 503 and 504).
+ # If you are seeing this behavior while using the default setting, reducing
+ # the number of threads will help.
+ default :cookbook_sync_threads, 10
+
+ # At the beginning of the Chef Client run, the cookbook manifests are downloaded which
+ # contain URLs for every file in every relevant cookbook. Most of the files
+ # (recipes, resources, providers, libraries, etc) are immediately synchronized
+ # at the start of the run. The handling of "files" and "templates" directories,
+ # however, have two modes of operation. They can either all be downloaded immediately
+ # at the start of the run (no_lazy_load==true) or else they can be lazily loaded as
+ # cookbook_file or template resources are converged which require them (no_lazy_load==false).
+ #
+ # The advantage of lazily loading these files is that unnecessary files are not
+ # synchronized. This may be useful to users with large files checked into cookbooks which
+ # are only selectively downloaded to a subset of clients which use the cookbook. However,
+ # better solutions are to either isolate large files into individual cookbooks and only
+ # include those cookbooks in the run lists of the servers that need them -- or move to
+ # using remote_file and a more appropriate backing store like S3 for large file
+ # distribution.
+ #
+ # The disadvantages of lazily loading files are that users some time find it
+ # confusing that their cookbooks are not fully synchronzied to the cache initially,
+ # and more importantly the time-sensitive URLs which are in the manifest may time
+ # out on long Chef runs before the resource that uses the file is converged
+ # (leading to many confusing 403 errors on template/cookbook_file resources).
+ #
+ default :no_lazy_load, true
+
+ # Default for the chef_gem compile_time attribute. Nil is the same as true but will emit
+ # warnings on every use of chef_gem prompting the user to be explicit. If the user sets this to
+ # true then the user will get backcompat behavior but with a single nag warning that cookbooks
+ # may break with this setting in the future. The false setting is the recommended setting and
+ # will become the default.
+ default :chef_gem_compile_time, nil
+
+ # A whitelisted array of attributes you want sent over the wire when node
+ # data is saved.
+ # The default setting is nil, which collects all data. Setting to [] will not
+ # collect any data for save.
+ default :automatic_attribute_whitelist, nil
+ default :default_attribute_whitelist, nil
+ default :normal_attribute_whitelist, nil
+ default :override_attribute_whitelist, nil
+
+ config_context :windows_service do
+ # Set `watchdog_timeout` to the number of seconds to wait for a chef-client run
+ # to finish
+ default :watchdog_timeout, 2 * (60 * 60) # 2 hours
+ end
+
+ # Chef requires an English-language UTF-8 locale to function properly. We attempt
+ # to use the 'locale -a' command and search through a list of preferences until we
+ # find one that we can use. On Ubuntu systems we should find 'C.UTF-8' and be
+ # able to use that even if there is no English locale on the server, but Mac, Solaris,
+ # AIX, etc do not have that locale. We then try to find an English locale and fall
+ # back to 'C' if we do not. The choice of fallback is pick-your-poison. If we try
+ # to do the work to return a non-US UTF-8 locale then we fail inside of providers when
+ # things like 'svn info' return Japanese and we can't parse them. OTOH, if we pick 'C' then
+ # we will blow up on UTF-8 characters. Between the warn we throw and the Encoding
+ # exception that ruby will throw it is more obvious what is broken if we drop UTF-8 by
+ # default rather than drop English.
+ #
+ # If there is no 'locale -a' then we return 'en_US.UTF-8' since that is the most commonly
+ # available English UTF-8 locale. However, all modern POSIXen should support 'locale -a'.
+ def self.guess_internal_locale
+ # https://github.com/opscode/chef/issues/2181
+ # Some systems have the `locale -a` command, but the result has
+ # invalid characters for the default encoding.
+ #
+ # For example, on CentOS 6 with ENV['LANG'] = "en_US.UTF-8",
+ # `locale -a`.split fails with ArgumentError invalid UTF-8 encoding.
+ cmd = Mixlib::ShellOut.new("locale -a").run_command
+ cmd.error!
+ locales = cmd.stdout.split
+ case
+ when locales.include?('C.UTF-8')
+ 'C.UTF-8'
+ when locales.include?('en_US.UTF-8'), locales.include?('en_US.utf8')
+ 'en_US.UTF-8'
+ when locales.include?('en.UTF-8')
+ 'en.UTF-8'
+ else
+ # Will match en_ZZ.UTF-8, en_ZZ.utf-8, en_ZZ.UTF8, en_ZZ.utf8
+ guesses = locales.select { |l| l =~ /^en_.*UTF-?8$/i }
+ unless guesses.empty?
+ guessed_locale = guesses.first
+ # Transform into the form en_ZZ.UTF-8
+ guessed_locale.gsub(/UTF-?8$/i, "UTF-8")
+ else
+ ChefConfig.logger.warn "Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support."
+ 'C'
+ end
+ end
+ rescue
+ if ChefConfig.windows?
+ ChefConfig.logger.debug "Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else."
+ else
+ ChefConfig.logger.debug "No usable locale -a command found, assuming you have en_US.UTF-8 installed."
+ end
+ 'en_US.UTF-8'
+ end
+
+ default :internal_locale, guess_internal_locale
+
+ # Force UTF-8 Encoding, for when we fire up in the 'C' locale or other strange locales (e.g.
+ # japanese windows encodings). If we do not do this, then knife upload will fail when a cookbook's
+ # README.md has UTF-8 characters that do not encode in whatever surrounding encoding we have been
+ # passed. Effectively, the Chef Ecosystem is globally UTF-8 by default. Anyone who wants to be
+ # able to upload Shift_JIS or ISO-8859-1 files needs to mark *those* files explicitly with
+ # magic tags to make ruby correctly identify the encoding being used. Changing this default will
+ # break Chef community cookbooks and is very highly discouraged.
+ default :ruby_encoding, Encoding::UTF_8
+
+ # If installed via an omnibus installer, this gives the path to the
+ # "embedded" directory which contains all of the software packaged with
+ # omnibus. This is used to locate the cacert.pem file on windows.
+ def self.embedded_dir
+ Pathname.new(_this_file).ascend do |path|
+ if path.basename.to_s == "embedded"
+ return path.to_s
+ end
+ end
+
+ nil
+ end
+
+ # Path to this file in the current install.
+ def self._this_file
+ File.expand_path(__FILE__)
+ end
+ end
+end
+
+
+
diff --git a/chef-config/lib/chef-config/exceptions.rb b/chef-config/lib/chef-config/exceptions.rb
new file mode 100644
index 0000000000..f5d76d856b
--- /dev/null
+++ b/chef-config/lib/chef-config/exceptions.rb
@@ -0,0 +1,26 @@
+#
+# Copyright:: Copyright (c) 2015 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-config/windows'
+require 'chef-config/logger'
+
+module ChefConfig
+
+ class InvalidPath < StandardError
+ end
+
+end
diff --git a/chef-config/lib/chef-config/logger.rb b/chef-config/lib/chef-config/logger.rb
new file mode 100644
index 0000000000..57f18809ee
--- /dev/null
+++ b/chef-config/lib/chef-config/logger.rb
@@ -0,0 +1,62 @@
+#
+# Copyright:: Copyright (c) 2015 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.
+#
+
+
+module ChefConfig
+
+ # Implements enough of Logger's API that we can use it in place of a real
+ # logger for `ChefConfig.logger`
+ class NullLogger
+
+ def <<(_msg)
+ end
+
+ def add(_severity, _message = nil, _progname = nil)
+ end
+
+ def debug(_progname = nil, &block)
+ end
+
+ def info(_progname = nil, &block)
+ end
+
+ def warn(_progname = nil, &block)
+ end
+
+ def deprecation(_progname = nil, &block)
+ end
+
+ def error(_progname = nil, &block)
+ end
+
+ def fatal(_progname = nil, &block)
+ end
+
+ end
+
+ @logger = NullLogger.new
+
+ def self.logger=(new_logger)
+ @logger = new_logger
+ end
+
+ def self.logger
+ @logger
+ end
+end
+
+
diff --git a/chef-config/lib/chef-config/path_helper.rb b/chef-config/lib/chef-config/path_helper.rb
new file mode 100644
index 0000000000..acc6b76377
--- /dev/null
+++ b/chef-config/lib/chef-config/path_helper.rb
@@ -0,0 +1,233 @@
+#
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# 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-config/windows'
+require 'chef-config/logger'
+require 'chef-config/exceptions'
+
+module ChefConfig
+ class PathHelper
+ # Maximum characters in a standard Windows path (260 including drive letter and NUL)
+ WIN_MAX_PATH = 259
+
+ def self.dirname(path)
+ if ChefConfig.windows?
+ # Find the first slash, not counting trailing slashes
+ end_slash = path.size
+ loop do
+ slash = path.rindex(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]/, end_slash - 1)
+ if !slash
+ return end_slash == path.size ? '.' : path_separator
+ elsif slash == end_slash - 1
+ end_slash = slash
+ else
+ return path[0..slash-1]
+ end
+ end
+ else
+ ::File.dirname(path)
+ end
+ end
+
+ BACKSLASH = '\\'.freeze
+
+ def self.path_separator
+ if ChefConfig.windows?
+ File::ALT_SEPARATOR || BACKSLASH
+ else
+ File::SEPARATOR
+ end
+ end
+
+ def self.join(*args)
+ path_separator_regex = Regexp.escape(File::SEPARATOR)
+ unless path_separator == File::SEPARATOR
+ path_separator_regex << Regexp.escape(path_separator)
+ end
+
+ trailing_slashes = /[#{path_separator_regex}]+$/
+ leading_slashes = /^[#{path_separator_regex}]+/
+
+ args.flatten.inject() do |joined_path, component|
+ joined_path = joined_path.sub(trailing_slashes, '')
+ component = component.sub(leading_slashes, '')
+ joined_path += "#{path_separator}#{component}"
+ end
+ end
+
+ def self.validate_path(path)
+ if ChefConfig.windows?
+ unless printable?(path)
+ msg = "Path '#{path}' contains non-printable characters. Check that backslashes are escaped with another backslash (e.g. C:\\\\Windows) in double-quoted strings."
+ ChefConfig.logger.error(msg)
+ raise ChefConfig::InvalidPath, msg
+ end
+
+ if windows_max_length_exceeded?(path)
+ ChefConfig.logger.debug("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'")
+ path.insert(0, "\\\\?\\")
+ end
+ end
+
+ path
+ end
+
+ def self.windows_max_length_exceeded?(path)
+ # Check to see if paths without the \\?\ prefix are over the maximum allowed length for the Windows API
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
+ unless path =~ /^\\\\?\\/
+ if path.length > WIN_MAX_PATH
+ return true
+ end
+ end
+
+ false
+ end
+
+ def self.printable?(string)
+ # returns true if string is free of non-printable characters (escape sequences)
+ # this returns false for whitespace escape sequences as well, e.g. \n\t
+ if string =~ /[^[:print:]]/
+ false
+ else
+ true
+ end
+ end
+
+ # Produces a comparable path.
+ def self.canonical_path(path, add_prefix=true)
+ # First remove extra separators and resolve any relative paths
+ abs_path = File.absolute_path(path)
+
+ if ChefConfig.windows?
+ # Add the \\?\ API prefix on Windows unless add_prefix is false
+ # Downcase on Windows where paths are still case-insensitive
+ abs_path.gsub!(::File::SEPARATOR, path_separator)
+ if add_prefix && abs_path !~ /^\\\\?\\/
+ abs_path.insert(0, "\\\\?\\")
+ end
+
+ abs_path.downcase!
+ end
+
+ abs_path
+ end
+
+ def self.cleanpath(path)
+ path = Pathname.new(path).cleanpath.to_s
+ # ensure all forward slashes are backslashes
+ if ChefConfig.windows?
+ path = path.gsub(File::SEPARATOR, path_separator)
+ end
+ path
+ end
+
+ def self.paths_eql?(path1, path2)
+ canonical_path(path1) == canonical_path(path2)
+ end
+
+ # Paths which may contain glob-reserved characters need
+ # to be escaped before globbing can be done.
+ # http://stackoverflow.com/questions/14127343
+ def self.escape_glob(*parts)
+ path = cleanpath(join(*parts))
+ path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x }
+ end
+
+ def self.relative_path_from(from, to)
+ Pathname.new(cleanpath(to)).relative_path_from(Pathname.new(cleanpath(from)))
+ end
+
+ # Retrieves the "home directory" of the current user while trying to ascertain the existence
+ # of said directory. The path returned uses / for all separators (the ruby standard format).
+ # If the home directory doesn't exist or an error is otherwise encountered, nil is returned.
+ #
+ # If a set of path elements is provided, they are appended as-is to the home path if the
+ # homepath exists.
+ #
+ # If an optional block is provided, the joined path is passed to that block if the home path is
+ # valid and the result of the block is returned instead.
+ #
+ # Home-path discovery is performed once. If a path is discovered, that value is memoized so
+ # that subsequent calls to home_dir don't bounce around.
+ #
+ # See self.all_homes.
+ def self.home(*args)
+ @@home_dir ||= self.all_homes { |p| break p }
+ if @@home_dir
+ path = File.join(@@home_dir, *args)
+ block_given? ? (yield path) : path
+ end
+ end
+
+ # See self.home. This method performs a similar operation except that it yields all the different
+ # possible values of 'HOME' that one could have on this platform. Hence, on windows, if
+ # HOMEDRIVE\HOMEPATH and USERPROFILE are different, the provided block will be called twice.
+ # This method goes out and checks the existence of each location at the time of the call.
+ #
+ # The return is a list of all the returned values from each block invocation or a list of paths
+ # if no block is provided.
+ def self.all_homes(*args)
+ paths = []
+ if ChefConfig.windows?
+ # By default, Ruby uses the the following environment variables to determine Dir.home:
+ # HOME
+ # HOMEDRIVE HOMEPATH
+ # USERPROFILE
+ # Ruby only checks to see if the variable is specified - not if the directory actually exists.
+ # On Windows, HOMEDRIVE HOMEPATH can point to a different location (such as an unavailable network mounted drive)
+ # while USERPROFILE points to the location where the user application settings and profile are stored. HOME
+ # is not defined as an environment variable (usually). If the home path actually uses UNC, then the prefix is
+ # HOMESHARE instead of HOMEDRIVE.
+ #
+ # We instead walk down the following and only include paths that actually exist.
+ # HOME
+ # HOMEDRIVE HOMEPATH
+ # HOMESHARE HOMEPATH
+ # USERPROFILE
+
+ paths << ENV['HOME']
+ paths << ENV['HOMEDRIVE'] + ENV['HOMEPATH'] if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
+ paths << ENV['HOMESHARE'] + ENV['HOMEPATH'] if ENV['HOMESHARE'] && ENV['HOMEPATH']
+ paths << ENV['USERPROFILE']
+ end
+ paths << Dir.home if ENV['HOME']
+
+ # Depending on what environment variables we're using, the slashes can go in any which way.
+ # Just change them all to / to keep things consistent.
+ # Note: Maybe this is a bad idea on some unixy systems where \ might be a valid character depending on
+ # the particular brand of kool-aid you consume. This code assumes that \ and / are both
+ # path separators on any system being used.
+ paths = paths.map { |home_path| home_path.gsub(path_separator, ::File::SEPARATOR) if home_path }
+
+ # Filter out duplicate paths and paths that don't exist.
+ valid_paths = paths.select { |home_path| home_path && Dir.exists?(home_path) }
+ valid_paths = valid_paths.uniq
+
+ # Join all optional path elements at the end.
+ # If a block is provided, invoke it - otherwise just return what we've got.
+ joined_paths = valid_paths.map { |home_path| File.join(home_path, *args) }
+ if block_given?
+ joined_paths.each { |p| yield p }
+ else
+ joined_paths
+ end
+ end
+ end
+end
+
diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb
new file mode 100644
index 0000000000..ffc0f707c9
--- /dev/null
+++ b/chef-config/lib/chef-config/version.rb
@@ -0,0 +1,4 @@
+# TODO: license header
+module ChefConfig
+ VERSION = '12.4.0.dev.0'
+end
diff --git a/chef-config/lib/chef-config/windows.rb b/chef-config/lib/chef-config/windows.rb
new file mode 100644
index 0000000000..a2e90067df
--- /dev/null
+++ b/chef-config/lib/chef-config/windows.rb
@@ -0,0 +1,29 @@
+#
+# Copyright:: Copyright (c) 2015 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.
+#
+
+module ChefConfig
+
+ def self.windows?
+ if RUBY_PLATFORM =~ /mswin|mingw|windows/
+ true
+ else
+ false
+ end
+ end
+
+end
+
diff --git a/chef-config/spec/spec_helper.rb b/chef-config/spec/spec_helper.rb
new file mode 100644
index 0000000000..df9461cde9
--- /dev/null
+++ b/chef-config/spec/spec_helper.rb
@@ -0,0 +1,75 @@
+require 'chef-config/windows'
+
+# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+RSpec.configure do |config|
+ # rspec-expectations config goes here. You can use an alternate
+ # assertion/expectation library such as wrong or the stdlib/minitest
+ # assertions if you prefer.
+ config.expect_with :rspec do |expectations|
+ # This option will default to `true` in RSpec 4. It makes the `description`
+ # and `failure_message` of custom matchers include text for helper methods
+ # defined using `chain`, e.g.:
+ # be_bigger_than(2).and_smaller_than(4).description
+ # # => "be bigger than 2 and smaller than 4"
+ # ...rather than:
+ # # => "be bigger than 2"
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ # rspec-mocks config goes here. You can use an alternate test double
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
+ config.mock_with :rspec do |mocks|
+ # Prevents you from mocking or stubbing a method that does not exist on
+ # a real object. This is generally recommended, and will default to
+ # `true` in RSpec 4.
+ mocks.verify_partial_doubles = true
+ end
+
+ # These two settings work together to allow you to limit a spec run
+ # to individual examples or groups you care about by tagging them with
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
+ # get run.
+ config.filter_run :focus
+ config.run_all_when_everything_filtered = true
+
+ config.filter_run_excluding :windows_only => true unless ChefConfig.windows?
+ config.filter_run_excluding :unix_only => true if ChefConfig.windows?
+
+ # Limits the available syntax to the non-monkey patched syntax that is
+ # recommended. For more details, see:
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
+ config.disable_monkey_patching!
+
+ # This setting enables warnings. It's recommended, but in some cases may
+ # be too noisy due to issues in dependencies.
+ config.warnings = true
+
+ # Many RSpec users commonly either run the entire suite or an individual
+ # file, and it's useful to allow more verbose output when running an
+ # individual spec file.
+ if config.files_to_run.one?
+ # Use the documentation formatter for detailed output,
+ # unless a formatter has already been configured
+ # (e.g. via a command-line flag).
+ config.default_formatter = 'doc'
+ end
+
+ # Print the 10 slowest examples and example groups at the
+ # end of the spec run, to help surface which specs are running
+ # particularly slow.
+ # config.profile_examples = 10
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ config.order = :random
+
+ # Seed global randomization in this process using the `--seed` CLI option.
+ # Setting this allows you to use `--seed` to deterministically reproduce
+ # test failures related to randomization by passing the same `--seed` value
+ # as the one that triggered the failure.
+ Kernel.srand config.seed
+end
diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb
new file mode 100644
index 0000000000..395fa2618e
--- /dev/null
+++ b/chef-config/spec/unit/config_spec.rb
@@ -0,0 +1,581 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
+# Copyright:: Copyright (c) 2008 Opscode, 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-config/config'
+
+RSpec.describe ChefConfig::Config do
+ before(:each) do
+ ChefConfig::Config.reset
+
+ # By default, treat deprecation warnings as errors in tests.
+ ChefConfig::Config.treat_deprecation_warnings_as_errors(true)
+
+ # Set environment variable so the setting persists in child processes
+ ENV['CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS'] = "1"
+ end
+
+ describe "config attribute writer: chef_server_url" do
+ before do
+ ChefConfig::Config.chef_server_url = "https://junglist.gen.nz"
+ end
+
+ it "sets the server url" do
+ expect(ChefConfig::Config.chef_server_url).to eq("https://junglist.gen.nz")
+ end
+
+ context "when the url has a leading space" do
+ before do
+ ChefConfig::Config.chef_server_url = " https://junglist.gen.nz"
+ end
+
+ it "strips the space from the url when setting" do
+ expect(ChefConfig::Config.chef_server_url).to eq("https://junglist.gen.nz")
+ end
+
+ end
+
+ context "when the url is a frozen string" do
+ before do
+ ChefConfig::Config.chef_server_url = " https://junglist.gen.nz".freeze
+ end
+
+ it "strips the space from the url when setting without raising an error" do
+ expect(ChefConfig::Config.chef_server_url).to eq("https://junglist.gen.nz")
+ end
+ end
+ end
+
+ describe "when configuring formatters" do
+ # if TTY and not(force-logger)
+ # formatter = configured formatter or default formatter
+ # formatter goes to STDOUT/ERR
+ # if log file is writeable
+ # log level is configured level or info
+ # log location is file
+ # else
+ # log level is warn
+ # log location is STDERR
+ # end
+ # elsif not(TTY) and force formatter
+ # formatter = configured formatter or default formatter
+ # if log_location specified
+ # formatter goes to log_location
+ # else
+ # formatter goes to STDOUT/ERR
+ # end
+ # else
+ # formatter = "null"
+ # log_location = configured-value or defualt
+ # log_level = info or defualt
+ # end
+ #
+ it "has an empty list of formatters by default" do
+ expect(ChefConfig::Config.formatters).to eq([])
+ end
+
+ it "configures a formatter with a short name" do
+ ChefConfig::Config.add_formatter(:doc)
+ expect(ChefConfig::Config.formatters).to eq([[:doc, nil]])
+ end
+
+ it "configures a formatter with a file output" do
+ ChefConfig::Config.add_formatter(:doc, "/var/log/formatter.log")
+ expect(ChefConfig::Config.formatters).to eq([[:doc, "/var/log/formatter.log"]])
+ end
+
+ end
+
+ [ false, true ].each do |is_windows|
+
+ context "On #{is_windows ? 'Windows' : 'Unix'}" do
+ def to_platform(*args)
+ ChefConfig::Config.platform_specific_path(*args)
+ end
+
+ before :each do
+ allow(ChefConfig).to receive(:windows?).and_return(is_windows)
+ end
+
+ describe "class method: platform_specific_path" do
+ if is_windows
+ it "should return a windows path on windows systems" do
+ path = "/etc/chef/cookbooks"
+ allow(ChefConfig::Config).to receive(:env).and_return({ 'SYSTEMDRIVE' => 'C:' })
+ # match on a regex that looks for the base path with an optional
+ # system drive at the beginning (c:)
+ # system drive is not hardcoded b/c it can change and b/c it is not present on linux systems
+ expect(ChefConfig::Config.platform_specific_path(path)).to eq("C:\\chef\\cookbooks")
+ end
+ else
+ it "should return given path on non-windows systems" do
+ path = "/etc/chef/cookbooks"
+ expect(ChefConfig::Config.platform_specific_path(path)).to eq("/etc/chef/cookbooks")
+ end
+ end
+ end
+
+ describe "default values" do
+ let :primary_cache_path do
+ if is_windows
+ "#{ChefConfig::Config.env['SYSTEMDRIVE']}\\chef"
+ else
+ "/var/chef"
+ end
+ end
+
+ let :secondary_cache_path do
+ if is_windows
+ "#{ChefConfig::Config[:user_home]}\\.chef"
+ else
+ "#{ChefConfig::Config[:user_home]}/.chef"
+ end
+ end
+
+ before do
+ if is_windows
+ allow(ChefConfig::Config).to receive(:env).and_return({ 'SYSTEMDRIVE' => 'C:' })
+ ChefConfig::Config[:user_home] = 'C:\Users\charlie'
+ else
+ ChefConfig::Config[:user_home] = '/Users/charlie'
+ end
+
+ allow(ChefConfig::Config).to receive(:path_accessible?).and_return(false)
+ end
+
+ describe "ChefConfig::Config[:chef_server_root]" do
+ context "when chef_server_url isn't set manually" do
+ it "returns the default of 'https://localhost:443'" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq("https://localhost:443")
+ end
+ end
+
+ context "when chef_server_url matches '../organizations/*' without a trailing slash" do
+ before do
+ ChefConfig::Config[:chef_server_url] = "https://example.com/organizations/myorg"
+ end
+ it "returns the full URL without /organizations/*" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq("https://example.com")
+ end
+ end
+
+ context "when chef_server_url matches '../organizations/*' with a trailing slash" do
+ before do
+ ChefConfig::Config[:chef_server_url] = "https://example.com/organizations/myorg/"
+ end
+ it "returns the full URL without /organizations/*" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq("https://example.com")
+ end
+ end
+
+ context "when chef_server_url matches '..organizations..' but not '../organizations/*'" do
+ before do
+ ChefConfig::Config[:chef_server_url] = "https://organizations.com/organizations"
+ end
+ it "returns the full URL without any modifications" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq(ChefConfig::Config[:chef_server_url])
+ end
+ end
+
+ context "when chef_server_url is a standard URL without the string organization(s)" do
+ before do
+ ChefConfig::Config[:chef_server_url] = "https://example.com/some_other_string"
+ end
+ it "returns the full URL without any modifications" do
+ expect(ChefConfig::Config[:chef_server_root]).to eq(ChefConfig::Config[:chef_server_url])
+ end
+ end
+ end
+
+ describe "ChefConfig::Config[:cache_path]" do
+ context "when /var/chef exists and is accessible" do
+ it "defaults to /var/chef" do
+ allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var/chef")).and_return(true)
+ expect(ChefConfig::Config[:cache_path]).to eq(primary_cache_path)
+ end
+ end
+
+ context "when /var/chef does not exist and /var is accessible" do
+ it "defaults to /var/chef" do
+ allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false)
+ allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(true)
+ expect(ChefConfig::Config[:cache_path]).to eq(primary_cache_path)
+ end
+ end
+
+ context "when /var/chef does not exist and /var is not accessible" do
+ it "defaults to $HOME/.chef" do
+ allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false)
+ allow(ChefConfig::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(false)
+ expect(ChefConfig::Config[:cache_path]).to eq(secondary_cache_path)
+ end
+ end
+
+ context "when /var/chef exists and is not accessible" do
+ it "defaults to $HOME/.chef" do
+ allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(true)
+ allow(File).to receive(:readable?).with(to_platform("/var/chef")).and_return(true)
+ allow(File).to receive(:writable?).with(to_platform("/var/chef")).and_return(false)
+
+ expect(ChefConfig::Config[:cache_path]).to eq(secondary_cache_path)
+ end
+ end
+
+ context "when chef is running in local mode" do
+ before do
+ ChefConfig::Config.local_mode = true
+ end
+
+ context "and config_dir is /a/b/c" do
+ before do
+ ChefConfig::Config.config_dir to_platform('/a/b/c')
+ end
+
+ it "cache_path is /a/b/c/local-mode-cache" do
+ expect(ChefConfig::Config.cache_path).to eq(to_platform('/a/b/c/local-mode-cache'))
+ end
+ end
+
+ context "and config_dir is /a/b/c/" do
+ before do
+ ChefConfig::Config.config_dir to_platform('/a/b/c/')
+ end
+
+ it "cache_path is /a/b/c/local-mode-cache" do
+ expect(ChefConfig::Config.cache_path).to eq(to_platform('/a/b/c/local-mode-cache'))
+ end
+ end
+ end
+ end
+
+ it "ChefConfig::Config[:file_backup_path] defaults to /var/chef/backup" do
+ allow(ChefConfig::Config).to receive(:cache_path).and_return(primary_cache_path)
+ backup_path = is_windows ? "#{primary_cache_path}\\backup" : "#{primary_cache_path}/backup"
+ expect(ChefConfig::Config[:file_backup_path]).to eq(backup_path)
+ end
+
+ it "ChefConfig::Config[:ssl_verify_mode] defaults to :verify_peer" do
+ expect(ChefConfig::Config[:ssl_verify_mode]).to eq(:verify_peer)
+ end
+
+ it "ChefConfig::Config[:ssl_ca_path] defaults to nil" do
+ expect(ChefConfig::Config[:ssl_ca_path]).to be_nil
+ end
+
+ # On Windows, we'll detect an omnibus build and set this to the
+ # cacert.pem included in the package, but it's nil if you're on Windows
+ # w/o omnibus (e.g., doing development on Windows, custom build, etc.)
+ if !is_windows
+ it "ChefConfig::Config[:ssl_ca_file] defaults to nil" do
+ expect(ChefConfig::Config[:ssl_ca_file]).to be_nil
+ end
+ end
+
+ it "ChefConfig::Config[:data_bag_path] defaults to /var/chef/data_bags" do
+ allow(ChefConfig::Config).to receive(:cache_path).and_return(primary_cache_path)
+ data_bag_path = is_windows ? "#{primary_cache_path}\\data_bags" : "#{primary_cache_path}/data_bags"
+ expect(ChefConfig::Config[:data_bag_path]).to eq(data_bag_path)
+ end
+
+ it "ChefConfig::Config[:environment_path] defaults to /var/chef/environments" do
+ allow(ChefConfig::Config).to receive(:cache_path).and_return(primary_cache_path)
+ environment_path = is_windows ? "#{primary_cache_path}\\environments" : "#{primary_cache_path}/environments"
+ expect(ChefConfig::Config[:environment_path]).to eq(environment_path)
+ end
+
+ describe "setting the config dir" do
+
+ context "when the config file is /etc/chef/client.rb" do
+
+ before do
+ ChefConfig::Config.config_file = to_platform("/etc/chef/client.rb")
+ end
+
+ it "config_dir is /etc/chef" do
+ expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef"))
+ end
+
+ context "and chef is running in local mode" do
+ before do
+ ChefConfig::Config.local_mode = true
+ end
+
+ it "config_dir is /etc/chef" do
+ expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef"))
+ end
+ end
+
+ context "when config_dir is set to /other/config/dir/" do
+ before do
+ ChefConfig::Config.config_dir = to_platform("/other/config/dir/")
+ end
+
+ it "yields the explicit value" do
+ expect(ChefConfig::Config.config_dir).to eq(to_platform("/other/config/dir/"))
+ end
+ end
+
+ end
+
+ context "when the user's home dir is /home/charlie/" do
+ before do
+ ChefConfig::Config.user_home = to_platform("/home/charlie")
+ end
+
+ it "config_dir is /home/charlie/.chef/" do
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(to_platform("/home/charlie/.chef"), ''))
+ end
+
+ context "and chef is running in local mode" do
+ before do
+ ChefConfig::Config.local_mode = true
+ end
+
+ it "config_dir is /home/charlie/.chef/" do
+ expect(ChefConfig::Config.config_dir).to eq(ChefConfig::PathHelper.join(to_platform("/home/charlie/.chef"), ''))
+ end
+ end
+ end
+
+ end
+
+ if is_windows
+ describe "finding the windows embedded dir" do
+ let(:default_config_location) { "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" }
+ let(:alternate_install_location) { "c:/my/alternate/install/place/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" }
+ let(:non_omnibus_location) { "c:/my/dev/stuff/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" }
+
+ let(:default_ca_file) { "c:/opscode/chef/embedded/ssl/certs/cacert.pem" }
+
+ it "finds the embedded dir in the default location" do
+ allow(ChefConfig::Config).to receive(:_this_file).and_return(default_config_location)
+ expect(ChefConfig::Config.embedded_dir).to eq("c:/opscode/chef/embedded")
+ end
+
+ it "finds the embedded dir in a custom install location" do
+ allow(ChefConfig::Config).to receive(:_this_file).and_return(alternate_install_location)
+ expect(ChefConfig::Config.embedded_dir).to eq("c:/my/alternate/install/place/chef/embedded")
+ end
+
+ it "doesn't error when not in an omnibus install" do
+ allow(ChefConfig::Config).to receive(:_this_file).and_return(non_omnibus_location)
+ expect(ChefConfig::Config.embedded_dir).to be_nil
+ end
+
+ it "sets the ssl_ca_cert path if the cert file is available" do
+ allow(ChefConfig::Config).to receive(:_this_file).and_return(default_config_location)
+ allow(File).to receive(:exist?).with(default_ca_file).and_return(true)
+ expect(ChefConfig::Config.ssl_ca_file).to eq(default_ca_file)
+ end
+ end
+ end
+ end
+
+ describe "ChefConfig::Config[:user_home]" do
+ it "should set when HOME is provided" do
+ expected = to_platform("/home/kitten")
+ allow(ChefConfig::PathHelper).to receive(:home).and_return(expected)
+ expect(ChefConfig::Config[:user_home]).to eq(expected)
+ end
+
+ it "falls back to the current working directory when HOME and USERPROFILE is not set" do
+ allow(ChefConfig::PathHelper).to receive(:home).and_return(nil)
+ expect(ChefConfig::Config[:user_home]).to eq(Dir.pwd)
+ end
+ end
+
+ describe "ChefConfig::Config[:encrypted_data_bag_secret]" do
+ let(:db_secret_default_path){ to_platform("/etc/chef/encrypted_data_bag_secret") }
+
+ before do
+ allow(File).to receive(:exist?).with(db_secret_default_path).and_return(secret_exists)
+ end
+
+ context "/etc/chef/encrypted_data_bag_secret exists" do
+ let(:secret_exists) { true }
+ it "sets the value to /etc/chef/encrypted_data_bag_secret" do
+ expect(ChefConfig::Config[:encrypted_data_bag_secret]).to eq db_secret_default_path
+ end
+ end
+
+ context "/etc/chef/encrypted_data_bag_secret does not exist" do
+ let(:secret_exists) { false }
+ it "sets the value to nil" do
+ expect(ChefConfig::Config[:encrypted_data_bag_secret]).to be_nil
+ end
+ end
+ end
+
+ describe "ChefConfig::Config[:event_handlers]" do
+ it "sets a event_handlers to an empty array by default" do
+ expect(ChefConfig::Config[:event_handlers]).to eq([])
+ end
+ it "should be able to add custom handlers" do
+ o = Object.new
+ ChefConfig::Config[:event_handlers] << o
+ expect(ChefConfig::Config[:event_handlers]).to be_include(o)
+ end
+ end
+
+ describe "ChefConfig::Config[:user_valid_regex]" do
+ context "on a platform that is not Windows" do
+ it "allows one letter usernames" do
+ any_match = ChefConfig::Config[:user_valid_regex].any? { |regex| regex.match('a') }
+ expect(any_match).to be_truthy
+ end
+ end
+ end
+
+ describe "ChefConfig::Config[:internal_locale]" do
+ let(:shell_out) do
+ cmd = instance_double("Mixlib::ShellOut", exitstatus: 0, stdout: locales, error!: nil)
+ allow(cmd).to receive(:run_command).and_return(cmd)
+ cmd
+ end
+
+ let(:locales) { locale_array.join("\n") }
+
+ before do
+ allow(Mixlib::ShellOut).to receive(:new).with("locale -a").and_return(shell_out)
+ end
+
+ shared_examples_for "a suitable locale" do
+ it "returns an English UTF-8 locale" do
+ expect(ChefConfig.logger).to_not receive(:warn).with(/Please install an English UTF-8 locale for Chef to use/)
+ expect(ChefConfig.logger).to_not receive(:debug).with(/Defaulting to locale en_US.UTF-8 on Windows/)
+ expect(ChefConfig.logger).to_not receive(:debug).with(/No usable locale -a command found/)
+ expect(ChefConfig::Config.guess_internal_locale).to eq expected_locale
+ end
+ end
+
+ context "when the result includes 'C.UTF-8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { [expected_locale, "en_US.UTF-8"] }
+ let(:expected_locale) { "C.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en_US.UTF-8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { ["en_CA.UTF-8", expected_locale, "en_NZ.UTF-8"] }
+ let(:expected_locale) { "en_US.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en_US.utf8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { ["en_CA.utf8", "en_US.utf8", "en_NZ.utf8"] }
+ let(:expected_locale) { "en_US.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en.UTF-8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { ["en.ISO8859-1", expected_locale] }
+ let(:expected_locale) { "en.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en_*.UTF-8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { [expected_locale, "en_CA.UTF-8", "en_GB.UTF-8"] }
+ let(:expected_locale) { "en_AU.UTF-8" }
+ end
+ end
+
+ context "when the result includes 'en_*.utf8'" do
+ include_examples "a suitable locale" do
+ let(:locale_array) { ["en_AU.utf8", "en_CA.utf8", "en_GB.utf8"] }
+ let(:expected_locale) { "en_AU.UTF-8" }
+ end
+ end
+
+ context "when the result does not include 'en_*.UTF-8'" do
+ let(:locale_array) { ["af_ZA", "af_ZA.ISO8859-1", "af_ZA.ISO8859-15", "af_ZA.UTF-8"] }
+
+ it "should fall back to C locale" do
+ expect(ChefConfig.logger).to receive(:warn).with("Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.")
+ expect(ChefConfig::Config.guess_internal_locale).to eq 'C'
+ end
+ end
+
+ context "on error" do
+ let(:locale_array) { [] }
+
+ let(:shell_out_cmd) { instance_double("Mixlib::ShellOut") }
+
+ before do
+ allow(Mixlib::ShellOut).to receive(:new).and_return(shell_out_cmd)
+ allow(shell_out_cmd).to receive(:run_command)
+ allow(shell_out_cmd).to receive(:error!).and_raise(Mixlib::ShellOut::ShellCommandFailed, "this is an error")
+ end
+
+ it "should default to 'en_US.UTF-8'" do
+ if is_windows
+ expect(ChefConfig.logger).to receive(:debug).with("Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else.")
+ else
+ expect(ChefConfig.logger).to receive(:debug).with("No usable locale -a command found, assuming you have en_US.UTF-8 installed.")
+ end
+ expect(ChefConfig::Config.guess_internal_locale).to eq "en_US.UTF-8"
+ end
+ end
+ end
+ end
+ end
+
+ describe "Treating deprecation warnings as errors" do
+
+ context "when using our default RSpec configuration" do
+
+ it "defaults to treating deprecation warnings as errors" do
+ expect(ChefConfig::Config[:treat_deprecation_warnings_as_errors]).to be(true)
+ end
+
+ it "sets CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS environment variable" do
+ expect(ENV['CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS']).to eq("1")
+ end
+
+ it "treats deprecation warnings as errors in child processes when testing" do
+ # Doing a full integration test where we launch a child process is slow
+ # and liable to break for weird reasons (bundler env stuff, etc.), so
+ # we're just checking that the presence of the environment variable
+ # causes treat_deprecation_warnings_as_errors to be set to true after a
+ # config reset.
+ ChefConfig::Config.reset
+ expect(ChefConfig::Config[:treat_deprecation_warnings_as_errors]).to be(true)
+ end
+
+ end
+
+ context "outside of our test environment" do
+
+ before do
+ ENV.delete('CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS')
+ ChefConfig::Config.reset
+ end
+
+ it "defaults to NOT treating deprecation warnings as errors" do
+ expect(ChefConfig::Config[:treat_deprecation_warnings_as_errors]).to be(false)
+ end
+ end
+
+
+ end
+
+end
diff --git a/chef-config/spec/unit/path_helper_spec.rb b/chef-config/spec/unit/path_helper_spec.rb
new file mode 100644
index 0000000000..3e6213597a
--- /dev/null
+++ b/chef-config/spec/unit/path_helper_spec.rb
@@ -0,0 +1,291 @@
+#
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# 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-config/path_helper'
+require 'spec_helper'
+
+RSpec.describe ChefConfig::PathHelper do
+
+ let(:path_helper) { described_class }
+
+ shared_examples_for "common_functionality" do
+ describe "join" do
+
+ it "joins starting with '' resolve to absolute paths" do
+ expect(path_helper.join('', 'a', 'b')).to eq("#{path_helper.path_separator}a#{path_helper.path_separator}b")
+ end
+
+ it "joins ending with '' add a / to the end" do
+ expect(path_helper.join('a', 'b', '')).to eq("a#{path_helper.path_separator}b#{path_helper.path_separator}")
+ end
+
+ end
+
+ describe "dirname" do
+ it "dirname('abc') is '.'" do
+ expect(path_helper.dirname('abc')).to eq('.')
+ end
+ it "dirname('/') is '/'" do
+ expect(path_helper.dirname(path_helper.path_separator)).to eq(path_helper.path_separator)
+ end
+ it "dirname('a/b/c') is 'a/b'" do
+ expect(path_helper.dirname(path_helper.join('a', 'b', 'c'))).to eq(path_helper.join('a', 'b'))
+ end
+ it "dirname('a/b/c/') is 'a/b'" do
+ expect(path_helper.dirname(path_helper.join('a', 'b', 'c', ''))).to eq(path_helper.join('a', 'b'))
+ end
+ it "dirname('/a/b/c') is '/a/b'" do
+ expect(path_helper.dirname(path_helper.join('', 'a', 'b', 'c'))).to eq(path_helper.join('', 'a', 'b'))
+ end
+ end
+ end
+
+ context "on windows" do
+
+ before(:each) do
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ end
+
+ include_examples("common_functionality")
+
+ it "path_separator is \\" do
+ expect(path_helper.path_separator).to eq('\\')
+ end
+
+ describe "platform-specific #join behavior" do
+
+ it "joins components on Windows when some end with unix separators" do
+ expect(path_helper.join('C:\\foo/', "bar", "baz")).to eq('C:\\foo\\bar\\baz')
+ end
+
+ it "joins components when some end with separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expected = "C:#{expected}"
+ expect(path_helper.join('C:\\foo\\', "bar", "baz")).to eq(expected)
+ end
+
+ it "joins components when some end and start with separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expected = "C:#{expected}"
+ expect(path_helper.join('C:\\foo\\', "bar/", "/baz")).to eq(expected)
+ end
+
+ it "joins components that don't end in separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expected = "C:#{expected}"
+ expect(path_helper.join('C:\\foo', "bar", "baz")).to eq(expected)
+ end
+
+ end
+
+
+ it "cleanpath changes slashes into backslashes and leaves backslashes alone" do
+ expect(path_helper.cleanpath('/a/b\\c/d/')).to eq('\\a\\b\\c\\d')
+ end
+
+ it "cleanpath does not remove leading double backslash" do
+ expect(path_helper.cleanpath('\\\\a/b\\c/d/')).to eq('\\\\a\\b\\c\\d')
+ end
+
+ end
+
+ context "on unix" do
+
+ before(:each) do
+ allow(ChefConfig).to receive(:windows?).and_return(false)
+ end
+
+ include_examples("common_functionality")
+
+ it "path_separator is /" do
+ expect(path_helper.path_separator).to eq('/')
+ end
+
+ it "cleanpath removes extra slashes alone" do
+ expect(path_helper.cleanpath('/a///b/c/d/')).to eq('/a/b/c/d')
+ end
+
+ describe "platform-specific #join behavior" do
+
+ it "joins components when some end with separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expect(path_helper.join("/foo/", "bar", "baz")).to eq(expected)
+ end
+
+ it "joins components when some end and start with separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expect(path_helper.join("/foo/", "bar/", "/baz")).to eq(expected)
+ end
+
+ it "joins components that don't end in separators" do
+ expected = path_helper.cleanpath("/foo/bar/baz")
+ expect(path_helper.join("/foo", "bar", "baz")).to eq(expected)
+ end
+
+ end
+
+ end
+
+ describe "validate_path" do
+ context "on windows" do
+ before(:each) do
+ # pass by default
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(path_helper).to receive(:printable?).and_return(true)
+ allow(path_helper).to receive(:windows_max_length_exceeded?).and_return(false)
+ end
+
+ it "returns the path if the path passes the tests" do
+ expect(path_helper.validate_path("C:\\ThisIsRigged")).to eql("C:\\ThisIsRigged")
+ end
+
+ it "does not raise an error if everything looks great" do
+ expect { path_helper.validate_path("C:\\cool path\\dude.exe") }.not_to raise_error
+ end
+
+ it "raises an error if the path has invalid characters" do
+ allow(path_helper).to receive(:printable?).and_return(false)
+ expect { path_helper.validate_path("Newline!\n") }.to raise_error(ChefConfig::InvalidPath)
+ end
+
+ it "Adds the \\\\?\\ prefix if the path exceeds MAX_LENGTH and does not have it" do
+ long_path = "C:\\" + "a" * 250 + "\\" + "b" * 250
+ prefixed_long_path = "\\\\?\\" + long_path
+ allow(path_helper).to receive(:windows_max_length_exceeded?).and_return(true)
+ expect(path_helper.validate_path(long_path)).to eql(prefixed_long_path)
+ end
+ end
+ end
+
+ describe "windows_max_length_exceeded?" do
+ it "returns true if the path is too long (259 + NUL) for the API" do
+ expect(path_helper.windows_max_length_exceeded?("C:\\" + "a" * 250 + "\\" + "b" * 6)).to be_truthy
+ end
+
+ it "returns false if the path is not too long (259 + NUL) for the standard API" do
+ expect(path_helper.windows_max_length_exceeded?("C:\\" + "a" * 250 + "\\" + "b" * 5)).to be_falsey
+ end
+
+ it "returns false if the path is over 259 characters but uses the \\\\?\\ prefix" do
+ expect(path_helper.windows_max_length_exceeded?("\\\\?\\C:\\" + "a" * 250 + "\\" + "b" * 250)).to be_falsey
+ end
+ end
+
+ describe "printable?" do
+ it "returns true if the string contains no non-printable characters" do
+ expect(path_helper.printable?("C:\\Program Files (x86)\\Microsoft Office\\Files.lst")).to be_truthy
+ end
+
+ it "returns true when given 'abc' in unicode" do
+ expect(path_helper.printable?("\u0061\u0062\u0063")).to be_truthy
+ end
+
+ it "returns true when given japanese unicode" do
+ expect(path_helper.printable?("\uff86\uff87\uff88")).to be_truthy
+ end
+
+ it "returns false if the string contains a non-printable character" do
+ expect(path_helper.printable?("\my files\work\notes.txt")).to be_falsey
+ end
+
+ # This isn't necessarily a requirement, but here to be explicit about functionality.
+ it "returns false if the string contains a newline or tab" do
+ expect(path_helper.printable?("\tThere's no way,\n\t *no* way,\n\t that you came from my loins.\n")).to be_falsey
+ end
+ end
+
+ describe "canonical_path" do
+ context "on windows", :windows_only do
+ it "returns an absolute path with backslashes instead of slashes" do
+ expect(path_helper.canonical_path("\\\\?\\C:/windows/win.ini")).to eq("\\\\?\\c:\\windows\\win.ini")
+ end
+
+ it "adds the \\\\?\\ prefix if it is missing" do
+ expect(path_helper.canonical_path("C:/windows/win.ini")).to eq("\\\\?\\c:\\windows\\win.ini")
+ end
+
+ it "returns a lowercase path" do
+ expect(path_helper.canonical_path("\\\\?\\C:\\CASE\\INSENSITIVE")).to eq("\\\\?\\c:\\case\\insensitive")
+ end
+ end
+
+ context "not on windows", :unix_only do
+ it "returns a canonical path" do
+ expect(path_helper.canonical_path("/etc//apache.d/sites-enabled/../sites-available/default")).to eq("/etc/apache.d/sites-available/default")
+ end
+ end
+ end
+
+ describe "paths_eql?" do
+ it "returns true if the paths are the same" do
+ allow(path_helper).to receive(:canonical_path).with("bandit").and_return("c:/bandit/bandit")
+ allow(path_helper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit")
+ expect(path_helper.paths_eql?("bandit", "../bandit/bandit")).to be_truthy
+ end
+
+ it "returns false if the paths are different" do
+ allow(path_helper).to receive(:canonical_path).with("bandit").and_return("c:/Bo/Bandit")
+ allow(path_helper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit")
+ expect(path_helper.paths_eql?("bandit", "../bandit/bandit")).to be_falsey
+ end
+ end
+
+ describe "escape_glob" do
+ it "escapes characters reserved by glob" do
+ path = "C:\\this\\*path\\[needs]\\escaping?"
+ escaped_path = "C:\\\\this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?"
+ expect(path_helper.escape_glob(path)).to eq(escaped_path)
+ end
+
+ context "when given more than one argument" do
+ it "joins, cleanpaths, and escapes characters reserved by glob" do
+ args = ["this/*path", "[needs]", "escaping?"]
+ escaped_path = if ChefConfig.windows?
+ "this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?"
+ else
+ "this/\\*path/\\[needs\\]/escaping\\?"
+ end
+ expect(path_helper).to receive(:join).with(*args).and_call_original
+ expect(path_helper).to receive(:cleanpath).and_call_original
+ expect(path_helper.escape_glob(*args)).to eq(escaped_path)
+ end
+ end
+ end
+
+ describe "all_homes" do
+ before do
+ stub_const('ENV', env)
+ allow(ChefConfig).to receive(:windows?).and_return(is_windows)
+ end
+
+ context "on windows" do
+ let (:is_windows) { true }
+ end
+
+ context "on unix" do
+ let (:is_windows) { false }
+
+ context "when HOME is not set" do
+ let (:env) { {} }
+ it "returns an empty array" do
+ expect(path_helper.all_homes).to eq([])
+ end
+ end
+ end
+ end
+end
diff --git a/chef.gemspec b/chef.gemspec
index a3b25a11d8..fb9f640d88 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -15,7 +15,8 @@ Gem::Specification.new do |s|
s.required_ruby_version = ">= 2.0.0"
- s.add_dependency "mixlib-config", "~> 2.0"
+ s.add_dependency "chef-config", "~> #{Chef::VERSION}"
+
s.add_dependency "mixlib-cli", "~> 1.4"
s.add_dependency "mixlib-log", "~> 1.3"
s.add_dependency "mixlib-authentication", "~> 1.3"
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index d2d3c736c2..4c72f25af3 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -19,740 +19,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require 'chef/log'
-require 'chef/exceptions'
-require 'mixlib/config'
-require 'chef/util/selinux'
-require 'chef/util/path_helper'
-require 'pathname'
-require 'chef/mixin/shell_out'
+require 'chef-config/config'
class Chef
- class Config
-
- extend Mixlib::Config
- extend Chef::Mixin::ShellOut
-
- PathHelper = Chef::Util::PathHelper
-
- # Evaluates the given string as config.
- #
- # +filename+ is used for context in stacktraces, but doesn't need to be the name of an actual file.
- def self.from_string(string, filename)
- self.instance_eval(string, filename, 1)
- end
-
- # Manages the chef secret session key
- # === Returns
- # <newkey>:: A new or retrieved session key
- #
- def self.manage_secret_key
- newkey = nil
- if Chef::FileCache.has_key?("chef_server_cookie_id")
- newkey = Chef::FileCache.load("chef_server_cookie_id")
- else
- chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
- newkey = ""
- 40.times { |i| newkey << chars[rand(chars.size-1)] }
- Chef::FileCache.store("chef_server_cookie_id", newkey)
- end
- newkey
- end
-
- def self.inspect
- configuration.inspect
- end
-
- def self.platform_specific_path(path)
- path = PathHelper.cleanpath(path)
- if Chef::Platform.windows?
- # turns \etc\chef\client.rb and \var\chef\client.rb into C:/chef/client.rb
- if env['SYSTEMDRIVE'] && path[0] == '\\' && path.split('\\')[2] == 'chef'
- path = PathHelper.join(env['SYSTEMDRIVE'], path.split('\\', 3)[2])
- end
- end
- path
- end
-
- def self.add_formatter(name, file_path=nil)
- formatters << [name, file_path]
- end
-
- def self.add_event_logger(logger)
- event_handlers << logger
- end
-
- # Config file to load (client.rb, knife.rb, etc. defaults set differently in knife, chef-client, etc.)
- configurable(:config_file)
-
- default(:config_dir) do
- if config_file
- PathHelper.dirname(config_file)
- else
- PathHelper.join(user_home, ".chef", "")
- end
- end
-
- default :formatters, []
-
- # Override the config dispatch to set the value of multiple server options simultaneously
- #
- # === Parameters
- # url<String>:: String to be set for all of the chef-server-api URL's
- #
- configurable(:chef_server_url).writes_value { |url| url.to_s.strip }
-
- # When you are using ActiveSupport, they monkey-patch 'daemonize' into Kernel.
- # So while this is basically identical to what method_missing would do, we pull
- # it up here and get a real method written so that things get dispatched
- # properly.
- configurable(:daemonize).writes_value { |v| v }
-
- # The root where all local chef object data is stored. cookbooks, data bags,
- # environments are all assumed to be in separate directories under this.
- # chef-solo uses these directories for input data. knife commands
- # that upload or download files (such as knife upload, knife role from file,
- # etc.) work.
- default :chef_repo_path do
- if self.configuration[:cookbook_path]
- if self.configuration[:cookbook_path].kind_of?(String)
- File.expand_path('..', self.configuration[:cookbook_path])
- else
- self.configuration[:cookbook_path].map do |path|
- File.expand_path('..', path)
- end
- end
- else
- cache_path
- end
- end
-
- def self.find_chef_repo_path(cwd)
- # In local mode, we auto-discover the repo root by looking for a path with "cookbooks" under it.
- # This allows us to run config-free.
- path = cwd
- until File.directory?(PathHelper.join(path, "cookbooks"))
- new_path = File.expand_path('..', path)
- if new_path == path
- Chef::Log.warn("No cookbooks directory found at or above current directory. Assuming #{Dir.pwd}.")
- return Dir.pwd
- end
- path = new_path
- end
- Chef::Log.info("Auto-discovered chef repository at #{path}")
- path
- end
-
- def self.derive_path_from_chef_repo_path(child_path)
- if chef_repo_path.kind_of?(String)
- PathHelper.join(chef_repo_path, child_path)
- else
- chef_repo_path.map { |path| PathHelper.join(path, child_path)}
- end
- end
-
- # Location of acls on disk. String or array of strings.
- # Defaults to <chef_repo_path>/acls.
- # Only applies to Enterprise Chef commands.
- default(:acl_path) { derive_path_from_chef_repo_path('acls') }
-
- # Location of clients on disk. String or array of strings.
- # Defaults to <chef_repo_path>/acls.
- default(:client_path) { derive_path_from_chef_repo_path('clients') }
-
- # Location of cookbooks on disk. String or array of strings.
- # Defaults to <chef_repo_path>/cookbooks. If chef_repo_path
- # is not specified, this is set to [/var/chef/cookbooks, /var/chef/site-cookbooks]).
- default(:cookbook_path) do
- if self.configuration[:chef_repo_path]
- derive_path_from_chef_repo_path('cookbooks')
- else
- Array(derive_path_from_chef_repo_path('cookbooks')).flatten +
- Array(derive_path_from_chef_repo_path('site-cookbooks')).flatten
- end
- end
-
- # Location of containers on disk. String or array of strings.
- # Defaults to <chef_repo_path>/containers.
- # Only applies to Enterprise Chef commands.
- default(:container_path) { derive_path_from_chef_repo_path('containers') }
-
- # Location of data bags on disk. String or array of strings.
- # Defaults to <chef_repo_path>/data_bags.
- default(:data_bag_path) { derive_path_from_chef_repo_path('data_bags') }
-
- # Location of environments on disk. String or array of strings.
- # Defaults to <chef_repo_path>/environments.
- default(:environment_path) { derive_path_from_chef_repo_path('environments') }
-
- # Location of groups on disk. String or array of strings.
- # Defaults to <chef_repo_path>/groups.
- # Only applies to Enterprise Chef commands.
- default(:group_path) { derive_path_from_chef_repo_path('groups') }
-
- # Location of nodes on disk. String or array of strings.
- # Defaults to <chef_repo_path>/nodes.
- default(:node_path) { derive_path_from_chef_repo_path('nodes') }
-
- # Location of roles on disk. String or array of strings.
- # Defaults to <chef_repo_path>/roles.
- default(:role_path) { derive_path_from_chef_repo_path('roles') }
-
- # Location of users on disk. String or array of strings.
- # Defaults to <chef_repo_path>/users.
- # Does not apply to Enterprise Chef commands.
- default(:user_path) { derive_path_from_chef_repo_path('users') }
-
- # Location of policies on disk. String or array of strings.
- # Defaults to <chef_repo_path>/policies.
- default(:policy_path) { derive_path_from_chef_repo_path('policies') }
-
- # Turn on "path sanity" by default. See also: http://wiki.opscode.com/display/chef/User+Environment+PATH+Sanity
- default :enforce_path_sanity, true
-
- # Formatted Chef Client output is a beta feature, disabled by default:
- default :formatter, "null"
-
- # The number of times the client should retry when registering with the server
- default :client_registration_retries, 5
-
- # An array of paths to search for knife exec scripts if they aren't in the current directory
- default :script_path, []
-
- # The root of all caches (checksums, cache and backup). If local mode is on,
- # this is under the user's home directory.
- default(:cache_path) do
- if local_mode
- PathHelper.join(config_dir, 'local-mode-cache')
- else
- primary_cache_root = platform_specific_path("/var")
- primary_cache_path = platform_specific_path("/var/chef")
- # Use /var/chef as the cache path only if that folder exists and we can read and write
- # into it, or /var exists and we can read and write into it (we'll create /var/chef later).
- # Otherwise, we'll create .chef under the user's home directory and use that as
- # the cache path.
- unless path_accessible?(primary_cache_path) || path_accessible?(primary_cache_root)
- secondary_cache_path = PathHelper.join(user_home, '.chef')
- Chef::Log.info("Unable to access cache at #{primary_cache_path}. Switching cache to #{secondary_cache_path}")
- secondary_cache_path
- else
- primary_cache_path
- end
- end
- end
-
- # Returns true only if the path exists and is readable and writeable for the user.
- def self.path_accessible?(path)
- File.exists?(path) && File.readable?(path) && File.writable?(path)
- end
-
- # Where cookbook files are stored on the server (by content checksum)
- default(:checksum_path) { PathHelper.join(cache_path, "checksums") }
-
- # Where chef's cache files should be stored
- default(:file_cache_path) { PathHelper.join(cache_path, "cache") }
-
- # Where backups of chef-managed files should go
- default(:file_backup_path) { PathHelper.join(cache_path, "backup") }
-
- # The chef-client (or solo) lockfile.
- #
- # If your `file_cache_path` resides on a NFS (or non-flock()-supporting
- # fs), it's recommended to set this to something like
- # '/tmp/chef-client-running.pid'
- default(:lockfile) { PathHelper.join(file_cache_path, "chef-client-running.pid") }
-
- ## Daemonization Settings ##
- # What user should Chef run as?
- default :user, nil
- default :group, nil
- default :umask, 0022
-
- # Valid log_levels are:
- # * :debug
- # * :info
- # * :warn
- # * :fatal
- # These work as you'd expect. There is also a special `:auto` setting.
- # When set to :auto, Chef will auto adjust the log verbosity based on
- # context. When a tty is available (usually because the user is running chef
- # in a console), the log level is set to :warn, and output formatters are
- # used as the primary mode of output. When a tty is not available, the
- # logger is the primary mode of output, and the log level is set to :info
- default :log_level, :auto
-
- # Logging location as either an IO stream or string representing log file path
- default :log_location, STDOUT
-
- # Using `force_formatter` causes chef to default to formatter output when STDOUT is not a tty
- default :force_formatter, false
-
- # Using `force_logger` causes chef to default to logger output when STDOUT is a tty
- default :force_logger, false
-
- default :http_retry_count, 5
- default :http_retry_delay, 5
- default :interval, nil
- default :once, nil
- default :json_attribs, nil
- # toggle info level log items that can create a lot of output
- default :verbose_logging, true
- default :node_name, nil
- default :diff_disabled, false
- default :diff_filesize_threshold, 10000000
- default :diff_output_threshold, 1000000
- default :local_mode, false
-
- default :pid_file, nil
-
- # Whether Chef Zero local mode should bind to a port. All internal requests
- # will go through the socketless code path regardless, so the socket is
- # only needed if other processes will connect to the local mode server.
- #
- # For compatibility this is set to true but it will be changed to false in
- # the future.
- default :listen, true
-
- config_context :chef_zero do
- config_strict_mode true
- default(:enabled) { Chef::Config.local_mode }
- default :host, 'localhost'
- default :port, 8889.upto(9999) # Will try ports from 8889-9999 until one works
- end
-
- default :chef_server_url, "https://localhost:443"
- default(:chef_server_root) do
- # if the chef_server_url is a path to an organization, aka
- # 'some_url.../organizations/*' then remove the '/organization/*' by default
- if self.configuration[:chef_server_url] =~ /\/organizations\/\S*$/
- self.configuration[:chef_server_url].split('/')[0..-3].join('/')
- elsif self.configuration[:chef_server_url] # default to whatever chef_server_url is
- self.configuration[:chef_server_url]
- else
- "https://localhost:443"
- end
- end
-
- default :rest_timeout, 300
- default :yum_timeout, 900
- default :yum_lock_timeout, 30
- default :solo, false
- default :splay, nil
- default :why_run, false
- default :color, false
- default :client_fork, true
- default :ez, false
- default :enable_reporting, true
- default :enable_reporting_url_fatals, false
- # Possible values for :audit_mode
- # :enabled, :disabled, :audit_only,
- #
- # TODO: 11 Dec 2014: Currently audit-mode is an experimental feature
- # and is disabled by default. When users choose to enable audit-mode,
- # a warning is issued in application/client#reconfigure.
- # This can be removed when audit-mode is enabled by default.
- default :audit_mode, :disabled
-
- # Chef only needs ohai to run the hostname plugin for the most basic
- # functionality. If the rest of the ohai plugins are not needed (like in
- # most of our testing scenarios)
- default :minimal_ohai, false
-
- # Policyfile is an experimental feature where a node gets its run list and
- # cookbook version set from a single document on the server instead of
- # expanding the run list and having the server compute the cookbook version
- # set based on environment constraints.
- #
- # Because this feature is experimental, it is not recommended for
- # production use. Developent/release of this feature may not adhere to
- # semver guidelines.
- default :use_policyfile, false
-
- # Set these to enable SSL authentication / mutual-authentication
- # with the server
-
- # Client side SSL cert/key for mutual auth
- default :ssl_client_cert, nil
- default :ssl_client_key, nil
-
- # Whether or not to verify the SSL cert for all HTTPS requests. When set to
- # :verify_peer (default), all HTTPS requests will be validated regardless of other
- # SSL verification settings. When set to :verify_none no HTTPS requests will
- # be validated.
- default :ssl_verify_mode, :verify_peer
-
- # Whether or not to verify the SSL cert for HTTPS requests to the Chef
- # server API. If set to `true`, the server's cert will be validated
- # regardless of the :ssl_verify_mode setting. This is set to `true` when
- # running in local-mode.
- # NOTE: This is a workaround until verify_peer is enabled by default.
- default(:verify_api_cert) { Chef::Config.local_mode }
-
- # Path to the default CA bundle files.
- default :ssl_ca_path, nil
- default(:ssl_ca_file) do
- if Chef::Platform.windows? and embedded_path = embedded_dir
- cacert_path = File.join(embedded_path, "ssl/certs/cacert.pem")
- cacert_path if File.exist?(cacert_path)
- else
- nil
- end
- end
-
- # A directory that contains additional SSL certificates to trust. Any
- # certificates in this directory will be added to whatever CA bundle ruby
- # is using. Use this to add self-signed certs for your Chef Server or local
- # HTTP file servers.
- default(:trusted_certs_dir) { PathHelper.join(config_dir, "trusted_certs") }
-
- # Where should chef-solo download recipes from?
- default :recipe_url, nil
-
- # Sets the version of the signed header authentication protocol to use (see
- # the 'mixlib-authorization' project for more detail). Currently, versions
- # 1.0 and 1.1 are available; however, the chef-server must first be
- # upgraded to support version 1.1 before clients can begin using it.
- #
- # Version 1.1 of the protocol is required when using a `node_name` greater
- # than ~90 bytes (~90 ascii characters), so chef-client will automatically
- # switch to using version 1.1 when `node_name` is too large for the 1.0
- # protocol. If you intend to use large node names, ensure that your server
- # supports version 1.1. Automatic detection of large node names means that
- # users will generally not need to manually configure this.
- #
- # In the future, this configuration option may be replaced with an
- # automatic negotiation scheme.
- default :authentication_protocol_version, "1.0"
-
- # This key will be used to sign requests to the Chef server. This location
- # must be writable by Chef during initial setup when generating a client
- # identity on the server.
- #
- # The chef-server will look up the public key for the client using the
- # `node_name` of the client.
- #
- # If chef-zero is enabled, this defaults to nil (no authentication).
- default(:client_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/client.pem") }
-
- # When registering the client, should we allow the client key location to
- # be a symlink? eg: /etc/chef/client.pem -> /etc/chef/prod-client.pem
- # If the path of the key goes through a directory like /tmp this should
- # never be set to true or its possibly an easily exploitable security hole.
- default :follow_client_key_symlink, false
-
- # This secret is used to decrypt encrypted data bag items.
- default(:encrypted_data_bag_secret) do
- if File.exist?(platform_specific_path("/etc/chef/encrypted_data_bag_secret"))
- platform_specific_path("/etc/chef/encrypted_data_bag_secret")
- else
- nil
- end
- end
-
- # As of Chef 11.0, version "1" is the default encrypted data bag item
- # format. Version "2" is available which adds encrypt-then-mac protection.
- # To maintain compatibility, versions other than 1 must be opt-in.
- #
- # Set this to `2` if you have chef-client 11.6.0+ in your infrastructure.
- # Set this to `3` if you have chef-client 11.?.0+, ruby 2 and OpenSSL >= 1.0.1 in your infrastructure. (TODO)
- default :data_bag_encrypt_version, 1
-
- # When reading data bag items, any supported version is accepted. However,
- # if all encrypted data bags have been generated with the version 2 format,
- # it is recommended to disable support for earlier formats to improve
- # security. For example, the version 2 format is identical to version 1
- # except for the addition of an HMAC, so an attacker with MITM capability
- # could downgrade an encrypted data bag to version 1 as part of an attack.
- default :data_bag_decrypt_minimum_version, 0
-
- # If there is no file in the location given by `client_key`, chef-client
- # will temporarily use the "validator" identity to generate one. If the
- # `client_key` is not present and the `validation_key` is also not present,
- # chef-client will not be able to authenticate to the server.
- #
- # The `validation_key` is never used if the `client_key` exists.
- #
- # If chef-zero is enabled, this defaults to nil (no authentication).
- default(:validation_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/validation.pem") }
- default :validation_client_name, "chef-validator"
-
- # When creating a new client via the validation_client account, Chef 11
- # servers allow the client to generate a key pair locally and send the
- # public key to the server. This is more secure and helps offload work from
- # the server, enhancing scalability. If enabled and the remote server
- # implements only the Chef 10 API, client registration will not work
- # properly.
- #
- # The default value is `true`. Set to `false` to disable client-side key
- # generation (server generates client keys).
- default(:local_key_generation) { true }
-
- # Zypper package provider gpg checks. Set to true to enable package
- # gpg signature checking. This will be default in the
- # future. Setting to false disables the warnings.
- # Leaving this set to nil or false is a security hazard!
- default :zypper_check_gpg, nil
-
- # Report Handlers
- default :report_handlers, []
-
- # Event Handlers
- default :event_handlers, []
-
- default :disable_event_loggers, false
- default :event_loggers do
- evt_loggers = []
- if Chef::Platform::windows? and not Chef::Platform::windows_server_2003?
- evt_loggers << :win_evt
- end
- evt_loggers
- end
-
- # Exception Handlers
- default :exception_handlers, []
-
- # Start handlers
- default :start_handlers, []
-
- # Syntax Check Cache. Knife keeps track of files that is has already syntax
- # checked by storing files in this directory. `syntax_check_cache_path` is
- # the new (and preferred) configuration setting. If not set, knife will
- # fall back to using cache_options[:path], which is deprecated but exists in
- # many client configs generated by pre-Chef-11 bootstrappers.
- default(:syntax_check_cache_path) { cache_options[:path] }
-
- # Deprecated:
- # Move this to the default value of syntax_cache_path when this is removed.
- default(:cache_options) { { :path => PathHelper.join(config_dir, "syntaxcache") } }
-
- # Whether errors should be raised for deprecation warnings. When set to
- # `false` (the default setting), a warning is emitted but code using
- # deprecated methods/features/etc. should work normally otherwise. When set
- # to `true`, usage of deprecated methods/features will raise a
- # `DeprecatedFeatureError`. This is used by Chef's tests to ensure that
- # deprecated functionality is not used internally by Chef. End users
- # should generally leave this at the default setting (especially in
- # production), but it may be useful when testing cookbooks or other code if
- # the user wishes to aggressively address deprecations.
- default(:treat_deprecation_warnings_as_errors) do
- # Using an environment variable allows this setting to be inherited in
- # tests that spawn new processes.
- ENV.key?("CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS")
- end
-
- # knife configuration data
- config_context :knife do
- default :ssh_port, nil
- default :ssh_user, nil
- default :ssh_attribute, nil
- default :ssh_gateway, nil
- default :bootstrap_version, nil
- default :bootstrap_proxy, nil
- default :bootstrap_template, nil
- default :secret, nil
- default :secret_file, nil
- default :identity_file, nil
- default :host_key_verify, nil
- default :forward_agent, nil
- default :sort_status_reverse, nil
- default :hints, {}
- end
-
- def self.set_defaults_for_windows
- # Those lists of regular expressions define what chef considers a
- # valid user and group name
- # From http://technet.microsoft.com/en-us/library/cc776019(WS.10).aspx
- principal_valid_regex_part = '[^"\/\\\\\[\]\:;|=,+*?<>]+'
- default :user_valid_regex, [ /^(#{principal_valid_regex_part}\\)?#{principal_valid_regex_part}$/ ]
- default :group_valid_regex, [ /^(#{principal_valid_regex_part}\\)?#{principal_valid_regex_part}$/ ]
-
- default :fatal_windows_admin_check, false
- end
-
- def self.set_defaults_for_nix
- # Those lists of regular expressions define what chef considers a
- # valid user and group name
- #
- # user/group cannot start with '-', '+' or '~'
- # user/group cannot contain ':', ',' or non-space-whitespace or null byte
- # everything else is allowed (UTF-8, spaces, etc) and we delegate to your O/S useradd program to barf or not
- # copies: http://anonscm.debian.org/viewvc/pkg-shadow/debian/trunk/debian/patches/506_relaxed_usernames?view=markup
- default :user_valid_regex, [ /^[^-+~:,\t\r\n\f\0]+[^:,\t\r\n\f\0]*$/ ]
- default :group_valid_regex, [ /^[^-+~:,\t\r\n\f\0]+[^:,\t\r\n\f\0]*$/ ]
- end
-
- # Those lists of regular expressions define what chef considers a
- # valid user and group name
- if Chef::Platform.windows?
- set_defaults_for_windows
- else
- set_defaults_for_nix
- end
-
- # This provides a hook which rspec can stub so that we can avoid twiddling
- # global state in tests.
- def self.env
- ENV
- end
-
- def self.windows_home_path
- Chef::Log.deprecation("Chef::Config.windows_home_path is now deprecated. Consider using Chef::Util::PathHelper.home instead.")
- PathHelper.home
- end
-
- # returns a platform specific path to the user home dir if set, otherwise default to current directory.
- default( :user_home ) { PathHelper.home || Dir.pwd }
-
- # Enable file permission fixup for selinux. Fixup will be done
- # only if selinux is enabled in the system.
- default :enable_selinux_file_permission_fixup, true
-
- # Use atomic updates (i.e. move operation) while updating contents
- # of the files resources. When set to false copy operation is
- # used to update files.
- default :file_atomic_update, true
-
- # There are 3 possible values for this configuration setting.
- # true => file staging is done in the destination directory
- # false => file staging is done via tempfiles under ENV['TMP']
- # :auto => file staging will try using destination directory if possible and
- # will fall back to ENV['TMP'] if destination directory is not usable.
- default :file_staging_uses_destdir, :auto
-
- # Exit if another run is in progress and the chef-client is unable to
- # get the lock before time expires. If nil, no timeout is enforced. (Exits
- # immediately if 0.)
- default :run_lock_timeout, nil
-
- # Number of worker threads for syncing cookbooks in parallel. Increasing
- # this number can result in gateway errors from the server (namely 503 and 504).
- # If you are seeing this behavior while using the default setting, reducing
- # the number of threads will help.
- default :cookbook_sync_threads, 10
-
- # At the beginning of the Chef Client run, the cookbook manifests are downloaded which
- # contain URLs for every file in every relevant cookbook. Most of the files
- # (recipes, resources, providers, libraries, etc) are immediately synchronized
- # at the start of the run. The handling of "files" and "templates" directories,
- # however, have two modes of operation. They can either all be downloaded immediately
- # at the start of the run (no_lazy_load==true) or else they can be lazily loaded as
- # cookbook_file or template resources are converged which require them (no_lazy_load==false).
- #
- # The advantage of lazily loading these files is that unnecessary files are not
- # synchronized. This may be useful to users with large files checked into cookbooks which
- # are only selectively downloaded to a subset of clients which use the cookbook. However,
- # better solutions are to either isolate large files into individual cookbooks and only
- # include those cookbooks in the run lists of the servers that need them -- or move to
- # using remote_file and a more appropriate backing store like S3 for large file
- # distribution.
- #
- # The disadvantages of lazily loading files are that users some time find it
- # confusing that their cookbooks are not fully synchronzied to the cache initially,
- # and more importantly the time-sensitive URLs which are in the manifest may time
- # out on long Chef runs before the resource that uses the file is converged
- # (leading to many confusing 403 errors on template/cookbook_file resources).
- #
- default :no_lazy_load, true
-
- # Default for the chef_gem compile_time attribute. Nil is the same as true but will emit
- # warnings on every use of chef_gem prompting the user to be explicit. If the user sets this to
- # true then the user will get backcompat behavior but with a single nag warning that cookbooks
- # may break with this setting in the future. The false setting is the recommended setting and
- # will become the default.
- default :chef_gem_compile_time, nil
-
- # A whitelisted array of attributes you want sent over the wire when node
- # data is saved.
- # The default setting is nil, which collects all data. Setting to [] will not
- # collect any data for save.
- default :automatic_attribute_whitelist, nil
- default :default_attribute_whitelist, nil
- default :normal_attribute_whitelist, nil
- default :override_attribute_whitelist, nil
-
- config_context :windows_service do
- # Set `watchdog_timeout` to the number of seconds to wait for a chef-client run
- # to finish
- default :watchdog_timeout, 2 * (60 * 60) # 2 hours
- end
-
- # Chef requires an English-language UTF-8 locale to function properly. We attempt
- # to use the 'locale -a' command and search through a list of preferences until we
- # find one that we can use. On Ubuntu systems we should find 'C.UTF-8' and be
- # able to use that even if there is no English locale on the server, but Mac, Solaris,
- # AIX, etc do not have that locale. We then try to find an English locale and fall
- # back to 'C' if we do not. The choice of fallback is pick-your-poison. If we try
- # to do the work to return a non-US UTF-8 locale then we fail inside of providers when
- # things like 'svn info' return Japanese and we can't parse them. OTOH, if we pick 'C' then
- # we will blow up on UTF-8 characters. Between the warn we throw and the Encoding
- # exception that ruby will throw it is more obvious what is broken if we drop UTF-8 by
- # default rather than drop English.
- #
- # If there is no 'locale -a' then we return 'en_US.UTF-8' since that is the most commonly
- # available English UTF-8 locale. However, all modern POSIXen should support 'locale -a'.
- def self.guess_internal_locale
- # https://github.com/opscode/chef/issues/2181
- # Some systems have the `locale -a` command, but the result has
- # invalid characters for the default encoding.
- #
- # For example, on CentOS 6 with ENV['LANG'] = "en_US.UTF-8",
- # `locale -a`.split fails with ArgumentError invalid UTF-8 encoding.
- locales = shell_out_with_systems_locale!("locale -a").stdout.split
- case
- when locales.include?('C.UTF-8')
- 'C.UTF-8'
- when locales.include?('en_US.UTF-8'), locales.include?('en_US.utf8')
- 'en_US.UTF-8'
- when locales.include?('en.UTF-8')
- 'en.UTF-8'
- else
- # Will match en_ZZ.UTF-8, en_ZZ.utf-8, en_ZZ.UTF8, en_ZZ.utf8
- guesses = locales.select { |l| l =~ /^en_.*UTF-?8$/i }
- unless guesses.empty?
- guessed_locale = guesses.first
- # Transform into the form en_ZZ.UTF-8
- guessed_locale.gsub(/UTF-?8$/i, "UTF-8")
- else
- Chef::Log.warn "Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support."
- 'C'
- end
- end
- rescue
- if Chef::Platform.windows?
- Chef::Log.debug "Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else."
- else
- Chef::Log.debug "No usable locale -a command found, assuming you have en_US.UTF-8 installed."
- end
- 'en_US.UTF-8'
- end
-
- default :internal_locale, guess_internal_locale
-
- # Force UTF-8 Encoding, for when we fire up in the 'C' locale or other strange locales (e.g.
- # japanese windows encodings). If we do not do this, then knife upload will fail when a cookbook's
- # README.md has UTF-8 characters that do not encode in whatever surrounding encoding we have been
- # passed. Effectively, the Chef Ecosystem is globally UTF-8 by default. Anyone who wants to be
- # able to upload Shift_JIS or ISO-8859-1 files needs to mark *those* files explicitly with
- # magic tags to make ruby correctly identify the encoding being used. Changing this default will
- # break Chef community cookbooks and is very highly discouraged.
- default :ruby_encoding, Encoding::UTF_8
-
- # If installed via an omnibus installer, this gives the path to the
- # "embedded" directory which contains all of the software packaged with
- # omnibus. This is used to locate the cacert.pem file on windows.
- def self.embedded_dir
- Pathname.new(_this_file).ascend do |path|
- if path.basename.to_s == "embedded"
- return path.to_s
- end
- end
-
- nil
- end
-
- # Path to this file in the current install.
- def self._this_file
- File.expand_path(__FILE__)
- end
- end
+ Config = ChefConfig::Config
end
diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb
index fda8ad2e5e..4ce2c040a4 100644
--- a/lib/chef/provider/file.rb
+++ b/lib/chef/provider/file.rb
@@ -28,6 +28,7 @@ require 'chef/mixin/checksum'
require 'chef/mixin/file_class'
require 'chef/util/backup'
require 'chef/util/diff'
+require 'chef/util/selinux'
require 'chef/deprecation/provider/file'
require 'chef/deprecation/warnings'
require 'chef/file_content_management/deploy'