knife-essentials 0.7.6 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
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