summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <jkeiser@opscode.com>2013-06-04 21:49:20 -0700
committerJohn Keiser <jkeiser@opscode.com>2013-06-07 13:12:36 -0700
commitfa45b5705e6c3ed34ddd04da349dd7acecae583c (patch)
tree5499adab78236e9daabb22aa67d725edd21f1944
parent79ee4c54bd0cee40098d280996741b147d0519f4 (diff)
downloadchef-fa45b5705e6c3ed34ddd04da349dd7acecae583c.tar.gz
Move ChefFSDataStore and config out to where they can be used by non-knife clients
-rw-r--r--lib/chef/chef_fs/chef_fs_data_store.rb371
-rw-r--r--lib/chef/chef_fs/config.rb191
-rw-r--r--lib/chef/chef_fs/knife.rb174
3 files changed, 585 insertions, 151 deletions
diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb
new file mode 100644
index 0000000000..41e1ca59ea
--- /dev/null
+++ b/lib/chef/chef_fs/chef_fs_data_store.rb
@@ -0,0 +1,371 @@
+#
+# Author:: John Keiser (<jkeiser@opscode.com>)
+# Copyright:: Copyright (c) 2012 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 'chef_zero/data_store/memory_store'
+require 'chef_zero/data_store/data_already_exists_error'
+require 'chef_zero/data_store/data_not_found_error'
+require 'chef/chef_fs/file_pattern'
+require 'chef/chef_fs/file_system'
+require 'chef/chef_fs/file_system/not_found_error'
+require 'chef/chef_fs/file_system/memory_root'
+
+class Chef
+ module ChefFS
+ class ChefFSDataStore
+ def initialize(chef_fs)
+ @chef_fs = chef_fs
+ @memory_store = ChefZero::DataStore::MemoryStore.new
+ end
+
+ def chef_fs
+ @chef_fs.call
+ end
+
+ MEMORY_PATHS = %w(sandboxes file_store)
+
+ def create_dir(path, name, *options)
+ if use_memory_store?(path)
+ @memory_store.create_dir(path, name, *options)
+ else
+ with_dir(path) do |parent|
+ parent.create_child(chef_fs_filename(path + [name]), nil)
+ end
+ end
+ end
+
+ def create(path, name, data, *options)
+ if use_memory_store?(path)
+ @memory_store.create(path, name, data, *options)
+
+ elsif path[0] == 'cookbooks' && path.length == 2
+ # Do nothing. The entry gets created when the cookbook is created.
+
+ else
+ if !data.is_a?(String)
+ raise "set only works with strings"
+ end
+
+ with_dir(path) do |parent|
+ parent.create_child(chef_fs_filename(path + [name]), data)
+ end
+ end
+ end
+
+ def get(path, request=nil)
+ if use_memory_store?(path)
+ @memory_store.get(path)
+
+ elsif path[0] == 'file_store' && path[1] == 'repo'
+ entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path[2..-1].join('/'))
+ begin
+ entry.read
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
+ end
+
+ else
+ with_entry(path) do |entry|
+ if path[0] == 'cookbooks' && path.length == 3
+ # get /cookbooks/NAME/version
+ result = entry.chef_object.to_hash
+ result.each_pair do |key, value|
+ if value.is_a?(Array)
+ value.each do |file|
+ if file.is_a?(Hash) && file.has_key?('checksum')
+ relative = ['file_store', 'repo', 'cookbooks']
+ if Chef::Config.versioned_cookbooks
+ relative << "#{path[1]}-#{path[2]}"
+ else
+ relative << path[1]
+ end
+ relative = relative + file[:path].split('/')
+ file['url'] = ChefZero::RestBase::build_uri(request.base_uri, relative)
+ end
+ end
+ end
+ end
+ JSON.pretty_generate(result)
+
+ else
+ entry.read
+ end
+ end
+ end
+ end
+
+ def set(path, data, *options)
+ if use_memory_store?(path)
+ @memory_store.set(path, data, *options)
+ else
+ if !data.is_a?(String)
+ raise "set only works with strings: #{path} = #{data.inspect}"
+ end
+
+ # Write out the files!
+ if path[0] == 'cookbooks' && path.length == 3
+ write_cookbook(path, data, *options)
+ else
+ with_dir(path[0..-2]) do |parent|
+ parent.create_child(chef_fs_filename(path), data)
+ end
+ end
+ end
+ end
+
+ def delete(path)
+ if use_memory_store?(path)
+ @memory_store.delete(path)
+ else
+ with_entry(path) do |entry|
+ if path[0] == 'cookbooks' && path.length >= 3
+ entry.delete(true)
+ else
+ entry.delete
+ end
+ end
+ end
+ end
+
+ def delete_dir(path, *options)
+ if use_memory_store?(path)
+ @memory_store.delete_dir(path, *options)
+ else
+ with_entry(path) do |entry|
+ entry.delete(options.include?(:recursive))
+ end
+ end
+ end
+
+ def list(path)
+ if use_memory_store?(path)
+ @memory_store.list(path)
+
+ elsif path[0] == 'cookbooks' && path.length == 1
+ with_entry(path) do |entry|
+ begin
+ if Chef::Config.versioned_cookbooks
+ # /cookbooks/name-version -> /cookbooks/name
+ entry.children.map { |child| split_name_version(child.name)[0] }.uniq
+ else
+ entry.children.map { |child| child.name }
+ end
+ rescue Chef::ChefFS::FileSystem::NotFoundError
+ # If the cookbooks dir doesn't exist, we have no cookbooks (not 404)
+ []
+ end
+ end
+
+ elsif path[0] == 'cookbooks' && path.length == 2
+ if Chef::Config.versioned_cookbooks
+ # list /cookbooks/name = filter /cookbooks/name-version down to name
+ entry.children.map { |child| split_name_version(child.name) }.
+ select { |name, version| name == path[1] }.
+ map { |name, version| version }.to_a
+ else
+ # list /cookbooks/name = <single version>
+ version = get_single_cookbook_version(path)
+ [version]
+ end
+
+ else
+ with_entry(path) do |entry|
+ begin
+ entry.children.map { |c| zero_filename(c) }.sort
+ rescue Chef::ChefFS::FileSystem::NotFoundError
+ # /cookbooks, /data, etc. never return 404
+ if path_always_exists?(path)
+ []
+ else
+ raise
+ end
+ end
+ end
+ end
+ end
+
+ def exists?(path)
+ if use_memory_store?(path)
+ @memory_store.exists?(path)
+ else
+ path_always_exists?(path) || Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists?
+ end
+ end
+
+ def exists_dir?(path)
+ if use_memory_store?(path)
+ @memory_store.exists_dir?(path)
+ elsif path[0] == 'cookbooks' && path.length == 2
+ list([ path[0] ]).include?(path[1])
+ else
+ Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists?
+ end
+ end
+
+ private
+
+ def use_memory_store?(path)
+ return path[0] == 'sandboxes' || path[0] == 'file_store' && path[1] == 'checksums' || path == [ 'environments', '_default' ]
+ end
+
+ def write_cookbook(path, data, *options)
+ # Create a little Chef::ChefFS memory filesystem with the data
+ if Chef::Config.versioned_cookbooks
+ cookbook_path = "cookbooks/#{path[1]}-#{path[2]}"
+ else
+ cookbook_path = "cookbooks/#{path[1]}"
+ end
+ cookbook_fs = Chef::ChefFS::FileSystem::MemoryRoot.new('uploading')
+ cookbook = JSON.parse(data, :create_additions => false)
+ cookbook.each_pair do |key, value|
+ if value.is_a?(Array)
+ value.each do |file|
+ if file.is_a?(Hash) && file.has_key?('checksum')
+ file_data = @memory_store.get(['file_store', 'checksums', file['checksum']])
+ cookbook_fs.add_file("#{cookbook_path}/#{file['path']}", file_data)
+ end
+ end
+ end
+ end
+
+ # Use the copy/diff algorithm to copy it down so we don't destroy
+ # chefignored data. This is terribly un-thread-safe.
+ Chef::ChefFS::FileSystem.copy_to(Chef::ChefFS::FilePattern.new("/#{cookbook_path}"), cookbook_fs, chef_fs, nil, {:purge => true})
+ end
+
+ def split_name_version(entry_name)
+ name_version = entry_name.split('-')
+ name = name_version[0..-2].join('-')
+ version = name_version[-1]
+ [name,version]
+ end
+
+ def to_chef_fs_path(path)
+ _to_chef_fs_path(path).join('/')
+ end
+
+ def chef_fs_filename(path)
+ _to_chef_fs_path(path)[-1]
+ end
+
+ def _to_chef_fs_path(path)
+ if path[0] == 'data'
+ path = path.dup
+ path[0] = 'data_bags'
+ if path.length >= 3
+ path[2] = "#{path[2]}.json"
+ end
+ elsif path[0] == 'cookbooks'
+ if path.length == 2
+ raise ChefZero::DataStore::DataNotFoundError.new(path)
+ elsif Chef::Config.versioned_cookbooks
+ if path.length >= 3
+ # cookbooks/name/version -> cookbooks/name-version
+ path = [ path[0], "#{path[1]}-#{path[2]}" ] + path[3..-1]
+ end
+ else
+ if path.length >= 3
+ # cookbooks/name/version/... -> /cookbooks/name/... iff metadata says so
+ version = get_single_cookbook_version(path)
+ if path[2] == version
+ path = path[0..1] + path[3..-1]
+ else
+ raise ChefZero::DataStore::DataNotFoundError.new(path)
+ end
+ end
+ end
+ elsif path.length == 2
+ path = path.dup
+ path[1] = "#{path[1]}.json"
+ end
+ path
+ end
+
+ def to_zero_path(entry)
+ path = entry.path.split('/')[1..-1]
+ if path[0] == 'data_bags'
+ path = path.dup
+ path[0] = 'data'
+ if path.length >= 3
+ path[2] = path[2][0..-6]
+ end
+
+ elsif path[0] == 'cookbooks'
+ if Chef::Config.versioned_cookbooks
+ # cookbooks/name-version/... -> cookbooks/name/version/...
+ if path.length >= 2
+ name, version = split_name_version(path[1])
+ path = [ path[0], name, version ] + path[2..-1]
+ end
+ else
+ if path.length >= 2
+ # cookbooks/name/... -> cookbooks/name/version/...
+ version = get_single_cookbook_version(path)
+ path = path[0..1] + [version] + path[2..-1]
+ end
+ end
+
+ elsif path.length == 2 && path[0] != 'cookbooks'
+ path = path.dup
+ path[1] = path[1][0..-6]
+ end
+ path
+ end
+
+ def zero_filename(entry)
+ to_zero_path(entry)[-1]
+ end
+
+ def path_always_exists?(path)
+ return path.length == 1 && %w(clients cookbooks data environments nodes roles users).include?(path[0])
+ end
+
+ def with_entry(path)
+ begin
+ yield Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path))
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
+ end
+ end
+
+ def with_dir(path)
+ begin
+ yield get_dir(_to_chef_fs_path(path), true)
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
+ end
+ end
+
+ def get_dir(path, create=false)
+ result = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path.join('/'))
+ if result.exists?
+ result
+ elsif create
+ get_dir(path[0..-2], create).create_child(result.name, nil)
+ else
+ raise ChefZero::DataStore::DataNotFoundError.new(path)
+ end
+ end
+
+ def get_single_cookbook_version(path)
+ dir = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path[0..1].join('/'))
+ metadata = ChefZero::CookbookData.metadata_from(dir, path[1], nil, [])
+ metadata[:version] || '0.0.0'
+ end
+ end
+ end
+end
diff --git a/lib/chef/chef_fs/config.rb b/lib/chef/chef_fs/config.rb
new file mode 100644
index 0000000000..4a8ca26ee8
--- /dev/null
+++ b/lib/chef/chef_fs/config.rb
@@ -0,0 +1,191 @@
+#
+# Author:: John Keiser (<jkeiser@opscode.com>)
+# Copyright:: Copyright (c) 2012 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 'chef/log'
+require 'chef/chef_fs/path_utils'
+
+class Chef
+ module ChefFS
+ #
+ # Helpers to take Chef::Config and create chef_fs and local_fs from it
+ #
+ class Config
+ def initialize(chef_config = Chef::Config, cwd = Dir.pwd)
+ @chef_config = chef_config
+ @cwd = cwd
+ configure_repo_paths
+ end
+
+ PATH_VARIABLES = %w(acl_path client_path cookbook_path container_path data_bag_path environment_path group_path node_path role_path user_path)
+
+ def chef_fs
+ @chef_fs ||= create_chef_fs
+ end
+
+ def create_chef_fs
+ require 'chef/chef_fs/file_system/chef_server_root_dir'
+ Chef::ChefFS::FileSystem::ChefServerRootDir.new("remote", @chef_config)
+ end
+
+ def local_fs
+ @local_fs ||= create_local_fs
+ end
+
+ def create_local_fs
+ require 'chef/chef_fs/file_system/chef_repository_file_system_root_dir'
+ Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir.new(object_paths)
+ end
+
+ # Returns the given real path's location relative to the server root.
+ #
+ # If chef_repo is /home/jkeiser/chef_repo,
+ # and pwd is /home/jkeiser/chef_repo/cookbooks,
+ # server_path('blah') == '/cookbooks/blah'
+ # server_path('../roles/blah.json') == '/roles/blah'
+ # server_path('../../readme.txt') == nil
+ # server_path('*/*ab*') == '/cookbooks/*/*ab*'
+ # server_path('/home/jkeiser/chef_repo/cookbooks/blah') == '/cookbooks/blah'
+ # server_path('/home/*/chef_repo/cookbooks/blah') == nil
+ #
+ # If there are multiple paths (cookbooks, roles, data bags, etc. can all
+ # have separate paths), and cwd+the path reaches into one of them, we will
+ # return a path relative to that. Otherwise we will return a path to
+ # chef_repo.
+ #
+ # Globs are allowed as well, but globs outside server paths are NOT
+ # (presently) supported. See above examples. TODO support that.
+ #
+ # If the path does not reach into ANY specified directory, nil is returned.
+ def server_path(file_path)
+ pwd = File.expand_path(Dir.pwd)
+ absolute_path = Chef::ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd))
+
+ # Check all object paths (cookbooks_dir, data_bags_dir, etc.)
+ object_paths.each_pair do |name, paths|
+ paths.each do |path|
+ realest_path = Chef::ChefFS::PathUtils.realest_path(path)
+ if absolute_path[0,realest_path.length] == realest_path &&
+ (absolute_path.length == realest_path.length ||
+ absolute_path[realest_path.length,1] =~ /#{PathUtils.regexp_path_separator}/)
+ relative_path = Chef::ChefFS::PathUtils::relative_to(absolute_path, realest_path)
+ return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}"
+ end
+ end
+ end
+
+ # Check chef_repo_path
+ Array(@chef_config[:chef_repo_path]).flatten.each do |chef_repo_path|
+ realest_chef_repo_path = Chef::ChefFS::PathUtils.realest_path(chef_repo_path)
+ if absolute_path == realest_chef_repo_path
+ return '/'
+ end
+ end
+
+ nil
+ end
+
+ # The current directory, relative to server root
+ def base_path
+ @base_path ||= server_path(File.expand_path(@cwd))
+ end
+
+ # Print the given server path, relative to the current directory
+ def format_path(entry)
+ server_path = entry.path
+ if base_path && server_path[0,base_path.length] == base_path
+ if server_path == base_path
+ return "."
+ elsif server_path[base_path.length,1] == "/"
+ return server_path[base_path.length + 1, server_path.length - base_path.length - 1]
+ elsif base_path == "/" && server_path[0,1] == "/"
+ return server_path[1, server_path.length - 1]
+ end
+ end
+ server_path
+ end
+
+ private
+
+ def object_paths
+ @object_paths ||= begin
+ if !@chef_config[:chef_repo_path]
+ Chef::Log.error("Must specify either chef_repo_path or cookbook_path in Chef config file")
+ exit(1)
+ end
+
+ result = {}
+ case @chef_config[:repo_mode]
+ when 'static'
+ object_names = %w(cookbooks data_bags environments roles)
+ when 'hosted_everything'
+ object_names = %w(acls clients cookbooks containers data_bags environments groups nodes roles)
+ else
+ object_names = %w(clients cookbooks data_bags environments nodes roles users)
+ end
+ object_names.each do |object_name|
+ variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
+ paths = Array(@chef_config[variable_name]).flatten
+ result[object_name] = paths.map { |path| File.expand_path(path) }
+ end
+ result
+ end
+ end
+
+ def configure_repo_paths
+ # Infer chef_repo_path from cookbook_path if not speciifed
+ if !@chef_config[:chef_repo_path]
+ if @chef_config[:cookbook_path]
+ @chef_config[:chef_repo_path] = Array(@chef_config[:cookbook_path]).flatten.map { |path| File.expand_path('..', path) }
+ end
+ end
+
+ # Smooth out some (for now) inappropriate defaults set by Chef
+ if @chef_config[:data_bag_path] == @chef_config.platform_specific_path('/var/chef/data_bags')
+ @chef_config[:data_bag_path] = nil
+ end
+ if @chef_config[:node_path] == '/var/chef/node'
+ @chef_config[:node_path] = nil
+ end
+ if @chef_config[:role_path] == @chef_config.platform_specific_path('/var/chef/roles')
+ @chef_config[:role_path] = nil
+ end
+
+ # Default to getting *everything* from the server.
+ if !@chef_config[:repo_mode]
+ if @chef_config[:chef_server_url] =~ /\/+organizations\/.+/
+ @chef_config[:repo_mode] = 'hosted_everything'
+ else
+ @chef_config[:repo_mode] = 'everything'
+ end
+ end
+
+ # Infer any *_path variables that are not specified
+ if @chef_config[:chef_repo_path]
+ PATH_VARIABLES.each do |variable_name|
+ chef_repo_paths = Array(@chef_config[:chef_repo_path]).flatten
+ variable = variable_name.to_sym
+ if !@chef_config[variable]
+ # cookbook_path -> cookbooks
+ @chef_config[variable] = chef_repo_paths.map { |path| File.join(path, "#{variable_name[0..-6]}s") }
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/chef_fs/knife.rb b/lib/chef/chef_fs/knife.rb
index f68cec8971..46b30cacbe 100644
--- a/lib/chef/chef_fs/knife.rb
+++ b/lib/chef/chef_fs/knife.rb
@@ -24,14 +24,20 @@ class Chef
# Workaround for CHEF-3932
def self.deps
super do
- require 'chef/chef_fs/file_system/chef_server_root_dir'
- require 'chef/chef_fs/file_system/chef_repository_file_system_root_dir'
+ require 'chef/config'
+ require 'chef/chef_fs/parallelizer'
+ require 'chef/chef_fs/config'
require 'chef/chef_fs/file_pattern'
require 'chef/chef_fs/path_utils'
- require 'chef/chef_fs/parallelizer'
- require 'chef/config'
yield
end
+ end
+
+ def self.inherited(c)
+ super
+ # Ensure we always get to do our includes, whether subclass calls deps or not
+ c.deps do
+ end
option :repo_mode,
:long => '--repo-mode MODE',
@@ -46,176 +52,38 @@ class Chef
:description => 'Maximum number of simultaneous requests to send (default: 10)'
end
- def self.inherited(c)
- super
- # Ensure we always get to do our includes, whether subclass calls deps or not
- c.deps do
- end
- end
-
def configure_chef
super
Chef::Config[:repo_mode] = config[:repo_mode] if config[:repo_mode]
Chef::Config[:concurrency] = config[:concurrency].to_i if config[:concurrency]
# --chef-repo-path overrides all other paths
- path_variables = %w(acl_path client_path cookbook_path container_path data_bag_path environment_path group_path node_path role_path user_path)
if config[:chef_repo_path]
Chef::Config[:chef_repo_path] = config[:chef_repo_path]
- path_variables.each do |variable_name|
- Chef::Config[variable_name.to_sym] = nil
- end
- end
-
- # Infer chef_repo_path from cookbook_path if not speciifed
- if !Chef::Config[:chef_repo_path]
- if Chef::Config[:cookbook_path]
- Chef::Config[:chef_repo_path] = Array(Chef::Config[:cookbook_path]).flatten.map { |path| File.expand_path('..', path) }
- end
- end
-
- # Smooth out some (for now) inappropriate defaults set by Chef
- if Chef::Config[:data_bag_path] == Chef::Config.platform_specific_path('/var/chef/data_bags')
- Chef::Config[:data_bag_path] = nil
- end
- if Chef::Config[:node_path] == '/var/chef/node'
- Chef::Config[:node_path] = nil
- end
- if Chef::Config[:role_path] == Chef::Config.platform_specific_path('/var/chef/roles')
- Chef::Config[:role_path] = nil
- end
-
- # Default to getting *everything* from the server.
- if !Chef::Config[:repo_mode]
- if Chef::Config[:chef_server_url] =~ /\/+organizations\/.+/
- Chef::Config[:repo_mode] = 'hosted_everything'
- else
- Chef::Config[:repo_mode] = 'everything'
+ Chef::ChefFS::Config::PATH_VARIABLES.each do |variable_name|
+ Chef::Config[variable_name.to_sym] = chef_repo_paths.map { |path| File.join(path, "#{variable_name[0..-6]}s") }
end
end
- # Infer any *_path variables that are not specified
- if Chef::Config[:chef_repo_path]
- path_variables.each do |variable_name|
- chef_repo_paths = Array(Chef::Config[:chef_repo_path]).flatten
- variable = variable_name.to_sym
- if !Chef::Config[variable]
- # cookbook_path -> cookbooks
- Chef::Config[variable] = chef_repo_paths.map { |path| File.join(path, "#{variable_name[0..-6]}s") }
- end
- end
- end
+ @chef_fs_config = Chef::ChefFS::Config.new(Chef::Config)
Chef::ChefFS::Parallelizer.threads = (Chef::Config[:concurrency] || 10) - 1
end
def chef_fs
- @chef_fs ||= create_chef_fs
+ @chef_fs_config.chef_fs
end
def create_chef_fs
- Chef::ChefFS::FileSystem::ChefServerRootDir.new("remote", Chef::Config)
- end
-
- def object_paths
- @object_paths ||= begin
- if !Chef::Config[:chef_repo_path]
- Chef::Log.error("Must specify either chef_repo_path or cookbook_path in Chef config file")
- exit(1)
- end
-
- result = {}
- case Chef::Config[:repo_mode]
- when 'static'
- object_names = %w(cookbooks data_bags environments roles)
- when 'hosted_everything'
- object_names = %w(acls clients cookbooks containers data_bags environments groups nodes roles)
- else
- object_names = %w(clients cookbooks data_bags environments nodes roles users)
- end
- object_names.each do |object_name|
- variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
- paths = Array(Chef::Config[variable_name]).flatten
- result[object_name] = paths.map { |path| File.expand_path(path) }
- end
- result
- end
- end
-
- # Returns the given real path's location relative to the server root.
- #
- # If chef_repo is /home/jkeiser/chef_repo,
- # and pwd is /home/jkeiser/chef_repo/cookbooks,
- # server_path('blah') == '/cookbooks/blah'
- # server_path('../roles/blah.json') == '/roles/blah'
- # server_path('../../readme.txt') == nil
- # server_path('*/*ab*') == '/cookbooks/*/*ab*'
- # server_path('/home/jkeiser/chef_repo/cookbooks/blah') == '/cookbooks/blah'
- # server_path('/home/*/chef_repo/cookbooks/blah') == nil
- #
- # If there are multiple paths (cookbooks, roles, data bags, etc. can all
- # have separate paths), and cwd+the path reaches into one of them, we will
- # return a path relative to that. Otherwise we will return a path to
- # chef_repo.
- #
- # Globs are allowed as well, but globs outside server paths are NOT
- # (presently) supported. See above examples. TODO support that.
- #
- # If the path does not reach into ANY specified directory, nil is returned.
- def server_path(file_path)
- pwd = File.expand_path(Dir.pwd)
- absolute_path = Chef::ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd))
-
- # Check all object paths (cookbooks_dir, data_bags_dir, etc.)
- object_paths.each_pair do |name, paths|
- paths.each do |path|
- realest_path = Chef::ChefFS::PathUtils.realest_path(path)
- if absolute_path[0,realest_path.length] == realest_path &&
- (absolute_path.length == realest_path.length ||
- absolute_path[realest_path.length,1] =~ /#{PathUtils.regexp_path_separator}/)
- relative_path = Chef::ChefFS::PathUtils::relative_to(absolute_path, realest_path)
- return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}"
- end
- end
- end
-
- # Check chef_repo_path
- Array(Chef::Config[:chef_repo_path]).flatten.each do |chef_repo_path|
- realest_chef_repo_path = Chef::ChefFS::PathUtils.realest_path(chef_repo_path)
- if absolute_path == realest_chef_repo_path
- return '/'
- end
- end
-
- nil
- end
-
- # The current directory, relative to server root
- def base_path
- @base_path ||= server_path(File.expand_path(Dir.pwd))
- end
-
- # Print the given server path, relative to the current directory
- def format_path(entry)
- server_path = entry.path
- if base_path && server_path[0,base_path.length] == base_path
- if server_path == base_path
- return "."
- elsif server_path[base_path.length,1] == "/"
- return server_path[base_path.length + 1, server_path.length - base_path.length - 1]
- elsif base_path == "/" && server_path[0,1] == "/"
- return server_path[1, server_path.length - 1]
- end
- end
- server_path
+ @chef_fs_config.create_chef_fs
end
def local_fs
- @local_fs ||= create_local_fs
+ @chef_fs_config.local_fs
end
def create_local_fs
- Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir.new(object_paths)
+ @chef_fs_config.create_local_fs
end
def pattern_args
@@ -226,14 +94,18 @@ class Chef
# TODO support absolute file paths and not just patterns? Too much?
# Could be super useful in a world with multiple repo paths
args.map do |arg|
- if !base_path && !PathUtils.is_absolute?(arg)
+ if !@chef_fs_config.base_path && !Chef::ChefFS::PathUtils.is_absolute?(arg)
ui.error("Attempt to use relative path '#{arg}' when current directory is outside the repository path")
exit(1)
end
- Chef::ChefFS::FilePattern::relative_to(base_path, arg)
+ Chef::ChefFS::FilePattern::relative_to(@chef_fs_config.base_path, arg)
end
end
+ def format_path(entry)
+ @chef_fs_config.format_path(entry)
+ end
+
def parallelize(inputs, options = {}, &block)
Chef::ChefFS::Parallelizer.parallelize(inputs, options, &block)
end