knife-essentials 0.7.6 → 0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. data/Rakefile +2 -2
  2. data/lib/chef/knife/{delete.rb → delete_essentials.rb} +10 -2
  3. data/lib/chef/knife/dependencies_essentials.rb +117 -0
  4. data/lib/chef/knife/{diff.rb → diff_essentials.rb} +3 -1
  5. data/lib/chef/knife/{download.rb → download_essentials.rb} +9 -4
  6. data/lib/chef/knife/{list.rb → list_essentials.rb} +8 -2
  7. data/lib/chef/knife/{raw.rb → raw_essentials.rb} +2 -0
  8. data/lib/chef/knife/{show.rb → show_essentials.rb} +3 -1
  9. data/lib/chef/knife/{upload.rb → upload_essentials.rb} +9 -4
  10. data/lib/chef_fs/command_line.rb +18 -0
  11. data/lib/chef_fs/file_pattern.rb +18 -0
  12. data/lib/chef_fs/file_system.rb +18 -1
  13. data/lib/chef_fs/file_system/base_fs_dir.rb +18 -0
  14. data/lib/chef_fs/file_system/base_fs_object.rb +19 -1
  15. data/lib/chef_fs/file_system/chef_repository_file_system_entry.rb +49 -8
  16. data/lib/chef_fs/file_system/chef_repository_file_system_root_dir.rb +62 -3
  17. data/lib/chef_fs/file_system/chef_server_root_dir.rb +18 -0
  18. data/lib/chef_fs/file_system/cookbook_dir.rb +26 -0
  19. data/lib/chef_fs/file_system/cookbook_file.rb +18 -0
  20. data/lib/chef_fs/file_system/cookbook_subdir.rb +18 -0
  21. data/lib/chef_fs/file_system/cookbooks_dir.rb +18 -2
  22. data/lib/chef_fs/file_system/data_bag_dir.rb +18 -0
  23. data/lib/chef_fs/file_system/data_bag_item.rb +18 -0
  24. data/lib/chef_fs/file_system/data_bags_dir.rb +18 -0
  25. data/lib/chef_fs/file_system/file_system_entry.rb +18 -0
  26. data/lib/chef_fs/file_system/file_system_error.rb +18 -0
  27. data/lib/chef_fs/file_system/file_system_root_dir.rb +18 -0
  28. data/lib/chef_fs/file_system/multiplexed_dir.rb +46 -0
  29. data/lib/chef_fs/file_system/must_delete_recursively_error.rb +18 -0
  30. data/lib/chef_fs/file_system/nodes_dir.rb +18 -0
  31. data/lib/chef_fs/file_system/nonexistent_fs_object.rb +18 -0
  32. data/lib/chef_fs/file_system/not_found_error.rb +18 -0
  33. data/lib/chef_fs/file_system/rest_list_dir.rb +18 -0
  34. data/lib/chef_fs/file_system/rest_list_entry.rb +18 -0
  35. data/lib/chef_fs/knife.rb +128 -18
  36. data/lib/chef_fs/path_utils.rb +19 -1
  37. data/lib/chef_fs/version.rb +1 -1
  38. data/spec/chef_fs/diff_spec.rb +69 -44
  39. data/spec/chef_fs/file_pattern_spec.rb +497 -479
  40. data/spec/chef_fs/file_system/chef_server_root_dir_spec.rb +18 -0
  41. data/spec/chef_fs/file_system/cookbooks_dir_spec.rb +18 -0
  42. data/spec/chef_fs/file_system/data_bags_dir_spec.rb +18 -0
  43. data/spec/chef_fs/file_system_spec.rb +124 -106
  44. data/spec/support/file_system_support.rb +93 -75
  45. metadata +12 -11
  46. data/lib/chef/sandbox_uploader.rb +0 -208
data/Rakefile CHANGED
@@ -1,8 +1,8 @@
1
1
  require 'bundler'
2
2
  require 'rubygems'
3
- require 'rake/gempackagetask'
4
- require 'rspec/core/rake_task'
3
+ require 'rubygems/package_task'
5
4
  require 'rdoc/task'
5
+ require 'rspec/core/rake_task'
6
6
 
7
7
  Bundler::GemHelper.install_tasks
8
8
 
@@ -3,7 +3,9 @@ require 'chef_fs/file_system'
3
3
 
4
4
  class Chef
5
5
  class Knife
6
- class Delete < ChefFS::Knife
6
+ remove_const(:Delete) if const_defined?(:Delete) # override Chef's version
7
+ class Delete < ::ChefFS::Knife
8
+ ChefFS = ::ChefFS
7
9
  banner "knife delete [PATTERN1 ... PATTERNn]"
8
10
 
9
11
  common_options
@@ -15,6 +17,12 @@ class Chef
15
17
  :description => "Delete directories recursively."
16
18
 
17
19
  def run
20
+ if name_args.length == 0
21
+ show_usage
22
+ ui.fatal("Must specify at least one argument. If you want to delete everything in this directory, type \"knife delete --recurse .\"")
23
+ exit 1
24
+ end
25
+
18
26
  # Get the matches (recursively)
19
27
  pattern_args.each do |pattern|
20
28
  ChefFS::FileSystem.list(chef_fs, pattern) do |result|
@@ -22,7 +30,7 @@ class Chef
22
30
  result.delete(config[:recurse])
23
31
  puts "Deleted #{result.path_for_printing}"
24
32
  rescue ChefFS::FileSystem::NotFoundError
25
- STDERR.puts "result.path_for_printing}: No such file or directory"
33
+ STDERR.puts "#{result.path_for_printing}: No such file or directory"
26
34
  end
27
35
  end
28
36
  end
@@ -0,0 +1,117 @@
1
+ require 'chef_fs/knife'
2
+ require 'chef_fs/file_system'
3
+
4
+ class Chef
5
+ class Knife
6
+ remove_const(:Dependencies) if const_defined?(:Dependencies) # override Chef's version
7
+ class Dependencies < ::ChefFS::Knife
8
+ ChefFS = ::ChefFS
9
+ banner "knife dependencies PATTERN1 [PATTERNn]"
10
+
11
+ common_options
12
+
13
+ option :recurse,
14
+ :long => '--[no-]recurse',
15
+ :boolean => true,
16
+ :description => "List dependencies recursively (default: true). Only works with --tree."
17
+ option :tree,
18
+ :long => '--tree',
19
+ :boolean => true,
20
+ :description => "Show dependencies in a visual tree. May show duplicates."
21
+ option :remote,
22
+ :long => '--remote',
23
+ :boolean => true,
24
+ :description => "List dependencies on the server instead of the local filesystem"
25
+
26
+ def run
27
+ if config[:tree] && config[:recurse]
28
+ STDERR.puts "--recurse requires --tree"
29
+ exit(1)
30
+ end
31
+ config[:recurse] = true if config[:recurse].nil?
32
+
33
+ @root = config[:remote] ? chef_fs : local_fs
34
+ dependencies = {}
35
+ pattern_args.each do |pattern|
36
+ ChefFS::FileSystem.list(@root, pattern) do |entry|
37
+ if config[:tree]
38
+ print_dependencies_tree(entry, dependencies)
39
+ else
40
+ print_flattened_dependencies(entry, dependencies)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def print_flattened_dependencies(entry, dependencies)
47
+ if !dependencies[entry.path]
48
+ dependencies[entry.path] = get_dependencies(entry)
49
+ dependencies[entry.path].each do |child|
50
+ child_entry = ChefFS::FileSystem.resolve_path(@root, child)
51
+ print_flattened_dependencies(child_entry, dependencies)
52
+ end
53
+ puts format_path(entry.path)
54
+ end
55
+ end
56
+
57
+ def print_dependencies_tree(entry, dependencies, printed = {}, depth = 0)
58
+ dependencies[entry.path] = get_dependencies(entry) if !dependencies[entry.path]
59
+ puts "#{' '*depth}#{format_path(entry.path)}"
60
+ if !printed[entry.path] && (config[:recurse] || depth <= 1)
61
+ printed[entry.path] = true
62
+ dependencies[entry.path].each do |child|
63
+ child_entry = ChefFS::FileSystem.resolve_path(@root, child)
64
+ print_dependencies_tree(child_entry, dependencies, printed, depth+1)
65
+ end
66
+ end
67
+ end
68
+
69
+ def get_dependencies(entry)
70
+ begin
71
+ object = entry.chef_object
72
+ rescue ChefFS::FileSystem::NotFoundError
73
+ STDERR.puts "#{result.path_for_printing}: No such file or directory"
74
+ return []
75
+ end
76
+ if !object
77
+ STDERR.puts "ERROR: #{entry} is not a Chef object!"
78
+ return []
79
+ end
80
+
81
+ if object.is_a?(Chef::CookbookVersion)
82
+ return object.metadata.dependencies.keys.map { |cookbook| "/cookbooks/#{cookbook}"}
83
+ elsif object.is_a?(Chef::Node)
84
+ return [ "/environments/#{object.chef_environment}.json" ] + dependencies_from_runlist(object.run_list)
85
+ elsif object.is_a?(Chef::Role)
86
+ result = []
87
+ object.env_run_lists.each_pair do |env,run_list|
88
+ dependencies_from_runlist(run_list).each do |dependency|
89
+ result << dependency if !result.include?(dependency)
90
+ end
91
+ end
92
+ return result
93
+ else
94
+ return []
95
+ end
96
+ end
97
+
98
+ def dependencies_from_runlist(run_list)
99
+ result = run_list.map do |run_list_item|
100
+ case run_list_item.type
101
+ when :role
102
+ "/roles/#{run_list_item.name}.json"
103
+ when :recipe
104
+ if run_list_item.name =~ /(.+)::[^:]*/
105
+ "/cookbooks/#{$1}"
106
+ else
107
+ "/cookbooks/#{run_list_item.name}"
108
+ end
109
+ else
110
+ raise "Unknown run list item type #{run_list_item.type}"
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
@@ -3,7 +3,9 @@ require 'chef_fs/command_line'
3
3
 
4
4
  class Chef
5
5
  class Knife
6
- class Diff < ChefFS::Knife
6
+ remove_const(:Diff) if const_defined?(:Diff) # override Chef's version
7
+ class Diff < ::ChefFS::Knife
8
+ ChefFS = ::ChefFS
7
9
  banner "knife diff PATTERNS"
8
10
 
9
11
  common_options
@@ -3,7 +3,9 @@ require 'chef_fs/command_line'
3
3
 
4
4
  class Chef
5
5
  class Knife
6
- class Download < ChefFS::Knife
6
+ remove_const(:Download) if const_defined?(:Download) # override Chef's version
7
+ class Download < ::ChefFS::Knife
8
+ ChefFS = ::ChefFS
7
9
  banner "knife download PATTERNS"
8
10
 
9
11
  common_options
@@ -34,10 +36,13 @@ class Chef
34
36
  :description => "Don't take action, only print what would happen"
35
37
 
36
38
  def run
37
- patterns = pattern_args_from(name_args.length > 0 ? name_args : [ "" ])
39
+ if name_args.length == 0
40
+ show_usage
41
+ ui.fatal("Must specify at least one argument. If you want to download everything in this directory, type \"knife download .\"")
42
+ exit 1
43
+ end
38
44
 
39
- # Get the matches (recursively)
40
- patterns.each do |pattern|
45
+ pattern_args.each do |pattern|
41
46
  ChefFS::FileSystem.copy_to(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1, config)
42
47
  end
43
48
  end
@@ -3,7 +3,9 @@ require 'chef_fs/file_system'
3
3
 
4
4
  class Chef
5
5
  class Knife
6
- class List < ChefFS::Knife
6
+ remove_const(:List) if const_defined?(:List) # override Chef's version
7
+ class List < ::ChefFS::Knife
8
+ ChefFS = ::ChefFS
7
9
  banner "knife list [-dR] [PATTERN1 ... PATTERNn]"
8
10
 
9
11
  common_options
@@ -16,6 +18,10 @@ class Chef
16
18
  :short => '-d',
17
19
  :boolean => true,
18
20
  :description => "When directories match the pattern, do not show the directories' children."
21
+ option :local,
22
+ :long => '--local',
23
+ :boolean => true,
24
+ :description => "List local directory instead of remote"
19
25
 
20
26
  def run
21
27
  patterns = name_args.length == 0 ? [""] : name_args
@@ -24,7 +30,7 @@ class Chef
24
30
  results = []
25
31
  dir_results = []
26
32
  pattern_args_from(patterns).each do |pattern|
27
- ChefFS::FileSystem.list(chef_fs, pattern) do |result|
33
+ ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern) do |result|
28
34
  if result.dir? && !config[:bare_directories]
29
35
  dir_results += add_dir_result(result)
30
36
  elsif result.exists?
@@ -2,7 +2,9 @@ require 'json'
2
2
 
3
3
  class Chef
4
4
  class Knife
5
+ remove_const(:Raw) if const_defined?(:Raw) # override Chef's version
5
6
  class Raw < Chef::Knife
7
+ ChefFS = ::ChefFS
6
8
  banner "knife raw REQUEST_PATH"
7
9
 
8
10
  option :method,
@@ -3,7 +3,9 @@ require 'chef_fs/file_system'
3
3
 
4
4
  class Chef
5
5
  class Knife
6
- class Show < ChefFS::Knife
6
+ remove_const(:Show) if const_defined?(:Show) # override Chef's version
7
+ class Show < ::ChefFS::Knife
8
+ ChefFS = ::ChefFS
7
9
  banner "knife show [PATTERN1 ... PATTERNn]"
8
10
 
9
11
  common_options
@@ -3,7 +3,9 @@ require 'chef_fs/command_line'
3
3
 
4
4
  class Chef
5
5
  class Knife
6
- class Upload < ChefFS::Knife
6
+ remove_const(:Upload) if const_defined?(:Upload) # override Chef's version
7
+ class Upload < ::ChefFS::Knife
8
+ ChefFS = ::ChefFS
7
9
  banner "knife upload PATTERNS"
8
10
 
9
11
  common_options
@@ -34,10 +36,13 @@ class Chef
34
36
  :description => "Don't take action, only print what would happen"
35
37
 
36
38
  def run
37
- patterns = pattern_args_from(name_args.length > 0 ? name_args : [ "" ])
39
+ if name_args.length == 0
40
+ show_usage
41
+ ui.fatal("Must specify at least one argument. If you want to upload everything in this directory, type \"knife upload .\"")
42
+ exit 1
43
+ end
38
44
 
39
- # Get the matches (recursively)
40
- patterns.each do |pattern|
45
+ pattern_args.each do |pattern|
41
46
  ChefFS::FileSystem.copy_to(pattern, local_fs, chef_fs, config[:recurse] ? nil : 1, config)
42
47
  end
43
48
  end
@@ -1,3 +1,21 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
1
19
  require 'chef_fs/file_system'
2
20
 
3
21
  module ChefFS
@@ -1,3 +1,21 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
1
19
  require 'chef_fs'
2
20
  require 'chef_fs/path_utils'
3
21
 
@@ -1,3 +1,21 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
1
19
  require 'chef_fs/path_utils'
2
20
 
3
21
  module ChefFS
@@ -336,4 +354,3 @@ module ChefFS
336
354
 
337
355
  end
338
356
  end
339
-
@@ -1,3 +1,21 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
1
19
  require 'chef_fs/file_system/base_fs_object'
2
20
  require 'chef_fs/file_system/nonexistent_fs_object'
3
21
 
@@ -1,3 +1,21 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
1
19
  require 'chef_fs/path_utils'
2
20
 
3
21
  module ChefFS
@@ -95,7 +113,7 @@ module ChefFS
95
113
 
96
114
  # Important directory attributes: name, parent, path, root
97
115
  # Overridable attributes: dir?, child(name), path_for_printing
98
- # Abstract: read, write, delete, children
116
+ # Abstract: read, write, delete, children, can_have_child?, create_child, compare_to
99
117
  end
100
118
  end
101
119
  end
@@ -1,3 +1,21 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
1
19
  require 'chef_fs/file_system/file_system_entry'
2
20
  require 'chef/cookbook/chefignore'
3
21
  require 'chef/cookbook/cookbook_version_loader'
@@ -10,22 +28,30 @@ require 'chef/client'
10
28
  module ChefFS
11
29
  module FileSystem
12
30
  # ChefRepositoryFileSystemEntry works just like FileSystemEntry,
13
- # except it pretends files in /cookbooks/chefignore don't exist
14
- # and it can inflate Chef objects
31
+ # except can inflate Chef objects
15
32
  class ChefRepositoryFileSystemEntry < FileSystemEntry
16
33
  def initialize(name, parent, file_path = nil)
17
34
  super(name, parent, file_path)
18
35
  # Load /cookbooks/chefignore
19
- if name == "cookbooks" && path == "/cookbooks" # We check name first because it's a faster fail than path
36
+ if path == '/cookbooks'
20
37
  @chefignore = Chef::Cookbook::Chefignore.new(self.file_path)
38
+ @ignore_empty_directories = true
39
+ # If we are a cookbook or a cookbook subdirectory, empty directories
40
+ # underneath us are ignored (since they cannot be uploaded)
41
+ elsif parent && parent.ignore_empty_directories?
42
+ @ignore_empty_directories = true
21
43
  end
22
44
  end
23
45
 
24
46
  attr_reader :chefignore
25
47
 
48
+ def ignore_empty_directories?
49
+ @ignore_empty_directories
50
+ end
51
+
26
52
  def chef_object
27
53
  begin
28
- if parent.path == "/cookbooks"
54
+ if parent.path == '/cookbooks'
29
55
  loader = Chef::Cookbook::CookbookVersionLoader.new(file_path, parent.chefignore)
30
56
  loader.load_cookbooks
31
57
  return loader.cookbook_version
@@ -40,22 +66,37 @@ module ChefFS
40
66
  end
41
67
 
42
68
  def children
43
- @children ||= Dir.entries(file_path).select { |entry| entry != '.' && entry != '..' && !ignored?(entry) }.
44
- map { |entry| ChefRepositoryFileSystemEntry.new(entry, self) }
69
+ @children ||=
70
+ Dir.entries(file_path).
71
+ select { |entry| entry != '.' && entry != '..' && !ignored?(entry) }.
72
+ map { |entry| ChefRepositoryFileSystemEntry.new(entry, self) }
45
73
  end
46
74
 
47
75
  attr_reader :chefignore
48
76
 
49
77
  private
50
78
 
79
+ def is_cookbooks_dir?
80
+ # We check name first because it's a faster fail than path
81
+ path == "/cookbooks"
82
+ end
83
+
51
84
  def ignored?(child_name)
52
- ignorer = self
85
+ # empty directories inside a cookbook are ignored
86
+ if ignore_empty_directories?
87
+ child_path = PathUtils.join(file_path, child_name)
88
+ if File.directory?(child_path) && Dir.entries(child_path) == [ '.', '..' ]
89
+ return true
90
+ end
91
+ end
92
+
93
+ ignorer = parent
53
94
  begin
54
95
  if ignorer.chefignore
55
96
  # Grab the path from entry to child
56
97
  path_to_child = child_name
57
98
  child = self
58
- while child != ignorer
99
+ while child.parent != ignorer
59
100
  path_to_child = PathUtils.join(child.name, path_to_child)
60
101
  child = child.parent
61
102
  end