diff options
author | jkeiser <jkeiser@opscode.com> | 2012-12-17 13:24:23 -0800 |
---|---|---|
committer | John Keiser <jkeiser@opscode.com> | 2013-06-07 13:12:12 -0700 |
commit | 02daf8327204f5229e432a8e2cf7ab94f1c434a9 (patch) | |
tree | 0440af7a0f73eac4d16b0bc1358236144bc95b2b /lib | |
parent | 777ad0c423a6f15804d328e407c014e627de5419 (diff) | |
download | chef-02daf8327204f5229e432a8e2cf7ab94f1c434a9.tar.gz |
Support chef_repo_path, roles_path, multiple cookbook_paths
Diffstat (limited to 'lib')
-rw-r--r-- | lib/chef/chef_fs/file_system/base_fs_object.rb | 2 | ||||
-rw-r--r-- | lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb | 23 | ||||
-rw-r--r-- | lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb | 47 | ||||
-rw-r--r-- | lib/chef/chef_fs/file_system/multiplexed_dir.rb | 48 | ||||
-rw-r--r-- | lib/chef/chef_fs/knife.rb | 128 | ||||
-rw-r--r-- | lib/chef/chef_fs/path_utils.rb | 2 |
6 files changed, 220 insertions, 30 deletions
diff --git a/lib/chef/chef_fs/file_system/base_fs_object.rb b/lib/chef/chef_fs/file_system/base_fs_object.rb index 855892fc89..9425b3546e 100644 --- a/lib/chef/chef_fs/file_system/base_fs_object.rb +++ b/lib/chef/chef_fs/file_system/base_fs_object.rb @@ -114,7 +114,7 @@ class Chef # Important directory attributes: name, parent, path, root # Overridable attributes: dir?, child(name), path_for_printing - # Abstract: read, write, delete, children + # Abstract: read, write, delete, children, can_have_child?, create_child, compare_to end end end diff --git a/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb b/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb index 87d904e830..277ebef168 100644 --- a/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb +++ b/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb @@ -29,17 +29,16 @@ class Chef module ChefFS module FileSystem # ChefRepositoryFileSystemEntry works just like FileSystemEntry, - # except it pretends files in /cookbooks/chefignore don't exist - # and it can inflate Chef objects + # except can inflate Chef objects class ChefRepositoryFileSystemEntry < FileSystemEntry def initialize(name, parent, file_path = nil) super(name, parent, file_path) # Load /cookbooks/chefignore - if name == "cookbooks" && path == "/cookbooks" # We check name first because it's a faster fail than path + if path == '/cookbooks' @chefignore = Chef::Cookbook::Chefignore.new(self.file_path) # If we are a cookbook or a cookbook subdirectory, empty directories # underneath us are ignored (since they cannot be uploaded) - elsif parent && parent.name === "cookbooks" && parent.path == "/cookbooks" + elsif parent.path == '/cookbooks' @ignore_empty_directories = true elsif parent && parent.ignore_empty_directories? @ignore_empty_directories = true @@ -54,7 +53,7 @@ class Chef def chef_object begin - if parent.path == "/cookbooks" + if parent.path == '/cookbooks' loader = Chef::Cookbook::CookbookVersionLoader.new(file_path, parent.chefignore) loader.load_cookbooks return loader.cookbook_version @@ -69,14 +68,24 @@ class Chef end def children - @children ||= Dir.entries(file_path).select { |entry| entry != '.' && entry != '..' && !ignored?(entry) }. - map { |entry| ChefRepositoryFileSystemEntry.new(entry, self) } + @children ||= + Dir.entries(file_path). + select { |entry| entry != '.' && entry != '..' && !ignored?(entry) }. + map { |entry| ChefRepositoryFileSystemEntry.new(entry, self) } end attr_reader :chefignore private + def is_cookbooks_dir? + # We check name first because it's a faster fail than path + path == "/cookbooks" + end + + def is_under_cookbooks? + end + def ignored?(child_name) # empty directories inside a cookbook are ignored if ignore_empty_directories? diff --git a/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb b/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb index fdad68003c..4ca674c627 100644 --- a/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb +++ b/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb @@ -16,14 +16,55 @@ # limitations under the License. # +require 'chef/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/chef_repository_file_system_entry' +require 'chef/chef_fs/file_system/multiplexed_dir' class Chef module ChefFS module FileSystem - class ChefRepositoryFileSystemRootDir < ChefRepositoryFileSystemEntry - def initialize(file_path) - super("", nil, file_path) + class ChefRepositoryFileSystemRootDir < BaseFSDir + def initialize(child_paths) + super("", nil) + @child_paths = child_paths + end + + attr_reader :child_paths + + def children + @children ||= child_paths.keys.map { |name| make_child_entry(name) }.select { |child| !child.nil? } + end + + def can_have_child?(name, is_dir) + child_paths.has_key?(name) && is_dir + end + + def create_child(name, file_contents = nil) + child_paths[name].each do |path| + Dir.mkdir(path) + end + make_child_entry(name) + end + + def ignore_empty_directories? + false + end + + def chefignore + nil + end + + private + + def make_child_entry(name) + paths = child_paths[name].select do |path| + File.exists?(path) + end + if paths.size == 0 + return nil + end + dirs = paths.map { |path| ChefRepositoryFileSystemEntry.new(name, self, path) } + MultiplexedDir.new(dirs) end end end diff --git a/lib/chef/chef_fs/file_system/multiplexed_dir.rb b/lib/chef/chef_fs/file_system/multiplexed_dir.rb new file mode 100644 index 0000000000..4e138ddf2a --- /dev/null +++ b/lib/chef/chef_fs/file_system/multiplexed_dir.rb @@ -0,0 +1,48 @@ +require 'chef/chef_fs/file_system/base_fs_object' +require 'chef/chef_fs/file_system/nonexistent_fs_object' + +class Chef + module ChefFS + module FileSystem + class MultiplexedDir < BaseFSDir + def initialize(*multiplexed_dirs) + @multiplexed_dirs = multiplexed_dirs.flatten + super(@multiplexed_dirs[0].name, @multiplexed_dirs[0].parent) + end + + attr_reader :multiplexed_dirs + + def write_dir + multiplexed_dirs[0] + end + + def children + @children ||= begin + result = [] + seen = {} + # If multiple things have the same name, the first one wins. + multiplexed_dirs.each do |dir| + dir.children.each do |child| + if seen[child.name] + Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{child} and #{seen[child.name]}") + else + result << child + seen[child.name] = child + end + end + end + result + end + end + + def can_have_child?(name, is_dir) + write_dir.can_have_child?(name, is_dir) + end + + def create_child(name, file_contents = nil) + write_dir.create_child(name, file_contents) + end + end + end + end +end diff --git a/lib/chef/chef_fs/knife.rb b/lib/chef/chef_fs/knife.rb index 8a116d980e..dc4b67e5ae 100644 --- a/lib/chef/chef_fs/knife.rb +++ b/lib/chef/chef_fs/knife.rb @@ -32,36 +32,124 @@ class Chef :description => "Specifies the local repository layout. Values: default or full" end - def base_path - @base_path ||= begin - relative_to_base = Chef::ChefFS::PathUtils::relative_to(File.expand_path(Dir.pwd), chef_repo) - relative_to_base == '.' ? '/' : "/#{relative_to_base}" + def chef_fs + @chef_fs ||= Chef::ChefFS::FileSystem::ChefServerRootDir.new("remote", Chef::Config, config[:repo_mode]) + end + + def chef_repo_path + @chef_repo_path ||= begin + if Chef::Config.chef_repo_path + File.expand_path(Chef::Config.chef_repo_path) + elsif Chef::Config.cookbook_path + File.expand_path('..', Array(Chef::Config.cookbook_path).flatten.first) + else + nil + end end end - def chef_fs - @chef_fs ||= Chef::ChefFS::FileSystem::ChefServerRootDir.new("remote", Chef::Config, config[:repo_mode]) + # Smooth out some inappropriate (for know) variable defaults in Chef. + def config_var(name) + case name + when :data_bag_path + Chef::Config[name] == Chef::Config.platform_specific_path('/var/chef/data_bags') ? nil : Chef::Config[name] + when :node_path + Chef::Config[name] == '/var/chef/node' ? nil : Chef::Config[name] + when :role_path + Chef::Config[name] == Chef::Config.platform_specific_path('/var/chef/roles') ? nil : Chef::Config[name] + when :chef_repo_path + chef_repo_path + else + Chef::Config[name] + end end - def chef_repo - @chef_repo ||= File.expand_path(File.join(Chef::Config.cookbook_path, "..")) + def object_paths + @object_paths ||= begin + result = {} + %w(clients cookbooks data_bags environments nodes roles users).each do |object_name| + variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path + paths = config_var(variable_name.to_sym) + if !paths + if !chef_repo_path + # TODO if chef_repo is not specified and repo_mode does not require + # clients/users/nodes, don't require them to be specified. + Chef::Log.error("Must specify either chef_repo_path or #{variable_name} in Chef config file") + exit(1) + end + paths = File.join(chef_repo_path, object_name) + end + paths = Array(paths).flatten.map { |path| File.expand_path(path) } + result[object_name] = paths + end + result + end end - def format_path(path) - if path[0,base_path.length] == base_path - if path == base_path + # 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 = 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| + if absolute_path[0,path.length] == path + relative_path = Chef::ChefFS::PathUtils::relative_to(path, absolute_path) + return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}" + end + end + end + + # Check chef_repo_path + if chef_repo_path[0,absolute_path.length] == absolute_path + relative_path = Chef::ChefFS::PathUtils::relative_to(chef_repo_path, absolute_path) + return relative_path == '.' ? '/' : "/#{relative_path}" + 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(server_path) + if server_path[0,base_path.length] == base_path + if server_path == base_path return "." - elsif path[base_path.length] == "/" - return path[base_path.length + 1, path.length - base_path.length - 1] - elsif base_path == "/" && path[0] == "/" - return path[1, path.length - 1] + elsif server_path[base_path.length] == "/" + return server_path[base_path.length + 1, server_path.length - base_path.length - 1] + elsif base_path == "/" && server_path[0] == "/" + return server_path[1, server_path.length - 1] end end - path + server_path end def local_fs - @local_fs ||= Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir.new(chef_repo) + @local_fs ||= Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir.new(object_paths) end def pattern_args @@ -69,7 +157,11 @@ class Chef end def pattern_args_from(args) - args.map { |arg| Chef::ChefFS::FilePattern::relative_to(base_path, arg) }.to_a + # 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| + Chef::ChefFS::FilePattern::relative_to(base_path, arg) + end end end diff --git a/lib/chef/chef_fs/path_utils.rb b/lib/chef/chef_fs/path_utils.rb index 67c62a7545..2f2a9537cf 100644 --- a/lib/chef/chef_fs/path_utils.rb +++ b/lib/chef/chef_fs/path_utils.rb @@ -24,7 +24,7 @@ class Chef # If you are in 'source', this is what you would have to type to reach 'dest' # relative_to('/a/b/c/d/e', '/a/b/x/y') == '../../c/d/e' - # relative_to('/a/b', '/a/b') == '' + # relative_to('/a/b', '/a/b') == '.' def self.relative_to(dest, source) # Skip past the common parts source_parts = Chef::ChefFS::PathUtils.split(source) |