knife-essentials 0.1

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 (34) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +114 -0
  3. data/Rakefile +24 -0
  4. data/lib/chef/knife/diff.rb +28 -0
  5. data/lib/chef/knife/list.rb +107 -0
  6. data/lib/chef/knife/show.rb +30 -0
  7. data/lib/chef_fs.rb +9 -0
  8. data/lib/chef_fs/command_line.rb +91 -0
  9. data/lib/chef_fs/diff.rb +158 -0
  10. data/lib/chef_fs/file_pattern.rb +292 -0
  11. data/lib/chef_fs/file_system.rb +69 -0
  12. data/lib/chef_fs/file_system/base_fs_dir.rb +23 -0
  13. data/lib/chef_fs/file_system/base_fs_object.rb +52 -0
  14. data/lib/chef_fs/file_system/chef_server_root_dir.rb +48 -0
  15. data/lib/chef_fs/file_system/cookbook_dir.rb +80 -0
  16. data/lib/chef_fs/file_system/cookbook_file.rb +32 -0
  17. data/lib/chef_fs/file_system/cookbook_subdir.rb +25 -0
  18. data/lib/chef_fs/file_system/cookbooks_dir.rb +21 -0
  19. data/lib/chef_fs/file_system/data_bag_dir.rb +22 -0
  20. data/lib/chef_fs/file_system/data_bags_dir.rb +23 -0
  21. data/lib/chef_fs/file_system/file_system_entry.rb +36 -0
  22. data/lib/chef_fs/file_system/file_system_root_dir.rb +11 -0
  23. data/lib/chef_fs/file_system/nonexistent_fs_object.rb +23 -0
  24. data/lib/chef_fs/file_system/not_found_exception.rb +12 -0
  25. data/lib/chef_fs/file_system/rest_list_dir.rb +42 -0
  26. data/lib/chef_fs/file_system/rest_list_entry.rb +72 -0
  27. data/lib/chef_fs/knife.rb +47 -0
  28. data/lib/chef_fs/path_utils.rb +43 -0
  29. data/lib/chef_fs/version.rb +4 -0
  30. data/spec/chef_fs/diff_spec.rb +263 -0
  31. data/spec/chef_fs/file_pattern_spec.rb +507 -0
  32. data/spec/chef_fs/file_system_spec.rb +117 -0
  33. data/spec/support/file_system_support.rb +108 -0
  34. metadata +79 -0
@@ -0,0 +1,69 @@
1
+ require 'chef_fs/path_utils'
2
+
3
+ module ChefFS
4
+ module FileSystem
5
+ # Yields a list of all things under (and including) this entry that match the
6
+ # given pattern.
7
+ #
8
+ # ==== Attributes
9
+ #
10
+ # * +entry+ - Entry to start listing under
11
+ # * +pattern+ - ChefFS::FilePattern to match children under
12
+ #
13
+ def self.list(entry, pattern, &block)
14
+ # Include self in results if it matches
15
+ if pattern.match?(entry.path)
16
+ block.call(entry)
17
+ end
18
+
19
+ if entry.dir? && pattern.could_match_children?(entry.path)
20
+ # If it's possible that our children could match, descend in and add matches.
21
+ exact_child_name = pattern.exact_child_name_under(entry.path)
22
+
23
+ # If we've got an exact name, don't bother listing children; just grab the
24
+ # child with the given name.
25
+ if exact_child_name
26
+ exact_child = entry.child(exact_child_name)
27
+ if exact_child
28
+ list(exact_child, pattern, &block)
29
+ end
30
+
31
+ # Otherwise, go through all children and find any matches
32
+ else
33
+ entry.children.each do |child|
34
+ list(child, pattern, &block)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ # Resolve the given path against the entry, returning
41
+ # the entry at the end of the path.
42
+ #
43
+ # ==== Attributes
44
+ #
45
+ # * +entry+ - the entry to start looking under. Relative
46
+ # paths will be resolved from here.
47
+ # * +path+ - the path to resolve. If it starts with +/+,
48
+ # the path will be resolved starting from +entry.root+.
49
+ #
50
+ # ==== Examples
51
+ #
52
+ # ChefFS::FileSystem.resolve_path(root_path, 'cookbooks/java/recipes/default.rb')
53
+ #
54
+ def self.resolve_path(entry, path)
55
+ return entry if path.length == 0
56
+ return resolve_path(entry.root, path) if path[0] == "/" && entry.root != entry
57
+ if path[0] == "/"
58
+ path = path[1,path.length-1]
59
+ end
60
+
61
+ result = entry
62
+ ChefFS::PathUtils::split(path).each do |part|
63
+ result = result.child(part)
64
+ end
65
+ result
66
+ end
67
+ end
68
+ end
69
+
@@ -0,0 +1,23 @@
1
+ require 'chef_fs/file_system/base_fs_object'
2
+ require 'chef_fs/file_system/nonexistent_fs_object'
3
+
4
+ module ChefFS
5
+ module FileSystem
6
+ class BaseFSDir < BaseFSObject
7
+ def initialize(name, parent)
8
+ super
9
+ end
10
+
11
+ def dir?
12
+ true
13
+ end
14
+
15
+ # Override child(name) to provide a child object by name without the network read
16
+ def child(name)
17
+ children.select { |child| child.name == name }.first || NonexistentFSObject.new(name, self)
18
+ end
19
+
20
+ # Abstract: children
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,52 @@
1
+ require 'chef_fs/path_utils'
2
+
3
+ module ChefFS
4
+ module FileSystem
5
+ class BaseFSObject
6
+ def initialize(name, parent)
7
+ @parent = parent
8
+ @name = name
9
+ if parent
10
+ @path = ChefFS::PathUtils::join(parent.path, name)
11
+ else
12
+ if name != ''
13
+ raise ArgumentError, "Name of root object must be empty string: was '#{name}' instead"
14
+ end
15
+ @path = '/'
16
+ end
17
+ end
18
+
19
+ attr_reader :name
20
+ attr_reader :parent
21
+ attr_reader :path
22
+
23
+ def root
24
+ parent ? parent.root : self
25
+ end
26
+
27
+ def path_for_printing
28
+ if parent
29
+ ChefFS::PathUtils::join(parent.path_for_printing, name)
30
+ else
31
+ name
32
+ end
33
+ end
34
+
35
+ def dir?
36
+ false
37
+ end
38
+
39
+ def exists?
40
+ true
41
+ end
42
+
43
+ def content_type
44
+ :text
45
+ end
46
+
47
+ # Important directory attributes: name, parent, path, root
48
+ # Overridable attributes: dir?, child(name), path_for_printing
49
+ # Abstract: read, write, delete, children
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,48 @@
1
+ require 'chef_fs/file_system/base_fs_dir'
2
+ require 'chef_fs/file_system/rest_list_dir'
3
+ require 'chef_fs/file_system/data_bags_dir'
4
+ require 'chef_fs/file_system/cookbooks_dir'
5
+
6
+ module ChefFS
7
+ module FileSystem
8
+ class ChefServerRootDir < BaseFSDir
9
+ def initialize(root_name, config)
10
+ super("", nil)
11
+ @chef_server_url = config[:chef_server_url]
12
+ @chef_username = config[:node_name]
13
+ @chef_private_key = config[:client_key]
14
+ @environment = config[:environment]
15
+ @root_name = root_name
16
+ end
17
+
18
+ attr_reader :chef_server_url
19
+ attr_reader :chef_username
20
+ attr_reader :chef_private_key
21
+ attr_reader :environment
22
+
23
+ def rest
24
+ Chef::REST.new(chef_server_url, chef_username, chef_private_key)
25
+ end
26
+
27
+ def api_path
28
+ ""
29
+ end
30
+
31
+ def path_for_printing
32
+ @root_name
33
+ end
34
+
35
+ def children
36
+ @children ||= [
37
+ RestListDir.new("clients", self),
38
+ CookbooksDir.new(self),
39
+ DataBagsDir.new(self),
40
+ RestListDir.new("environments", self),
41
+ RestListDir.new("nodes", self),
42
+ RestListDir.new("roles", self),
43
+ # RestListDir.new("sandboxes", self)
44
+ ]
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,80 @@
1
+ require 'chef_fs/file_system/rest_list_dir'
2
+ require 'chef_fs/file_system/cookbook_subdir'
3
+ require 'chef_fs/file_system/cookbook_file'
4
+ require 'chef/cookbook_version'
5
+
6
+ module ChefFS
7
+ module FileSystem
8
+ class CookbookDir < BaseFSDir
9
+ def initialize(name, parent, versions = nil)
10
+ super(name, parent)
11
+ @versions = versions
12
+ end
13
+
14
+ attr_reader :versions
15
+
16
+ def add_child(child)
17
+ @children << child
18
+ end
19
+
20
+ def api_path
21
+ "#{parent.api_path}/#{name}/_latest"
22
+ end
23
+
24
+ def child(name)
25
+ children.select { |child| child.name == name }.first || NonexistentFSObject.new(name, self)
26
+ end
27
+
28
+ def children
29
+ if @children.nil?
30
+ @children = []
31
+ Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment|
32
+ next unless manifest.has_key?(segment)
33
+ manifest[segment].each do |segment_file|
34
+ parts = segment_file['path'].split('/')
35
+ # Get or create the path to the file
36
+ container = self
37
+ parts[0,parts.length-1].each do |part|
38
+ old_container = container
39
+ container = old_container.children.select { |child| part == child.name }.first
40
+ if !container
41
+ container = CookbookSubdir.new(part, old_container)
42
+ old_container.add_child(container)
43
+ end
44
+ end
45
+ # Create the file itself
46
+ container.add_child(CookbookFile.new(parts[parts.length-1], container, segment_file))
47
+ end
48
+ end
49
+ end
50
+ @children
51
+ end
52
+
53
+ def exists?
54
+ if !@versions
55
+ child = parent.children.select { |child| child.name == name }.first
56
+ @versions = child.versions if child
57
+ end
58
+ !!@versions
59
+ end
60
+
61
+ def rest
62
+ parent.rest
63
+ end
64
+
65
+ private
66
+
67
+ def manifest
68
+ begin
69
+ @manifest ||= rest.get_rest(api_path).manifest
70
+ rescue Net::HTTPServerException
71
+ if $!.response.code == "404"
72
+ raise ChefFS::FileSystem::NotFoundException, $!
73
+ else
74
+ raise
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,32 @@
1
+ require 'chef_fs/file_system/base_fs_object'
2
+
3
+ module ChefFS
4
+ module FileSystem
5
+ class CookbookFile < BaseFSObject
6
+ def initialize(name, parent, file)
7
+ super(name, parent)
8
+ @file = file
9
+ end
10
+
11
+ attr_reader :file
12
+
13
+ def checksum
14
+ file[:checksum]
15
+ end
16
+
17
+ def read
18
+ old_sign_on_redirect = rest.sign_on_redirect
19
+ rest.sign_on_redirect = false
20
+ begin
21
+ rest.get_rest(file['url'])
22
+ ensure
23
+ rest.sign_on_redirect = true
24
+ end
25
+ end
26
+
27
+ def rest
28
+ parent.rest
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ require 'chef_fs/file_system/base_fs_dir'
2
+
3
+ # TODO: take environment into account
4
+
5
+ module ChefFS
6
+ module FileSystem
7
+ class CookbookSubdir < BaseFSDir
8
+ def initialize(name, parent)
9
+ super(name, parent)
10
+ @children = []
11
+ end
12
+
13
+ attr_reader :versions
14
+ attr_reader :children
15
+
16
+ def add_child(child)
17
+ @children << child
18
+ end
19
+
20
+ def rest
21
+ parent.rest
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ require 'chef_fs/file_system/rest_list_dir'
2
+ require 'chef_fs/file_system/cookbook_dir'
3
+
4
+ module ChefFS
5
+ module FileSystem
6
+ class CookbooksDir < RestListDir
7
+ def initialize(parent)
8
+ super("cookbooks", parent)
9
+ end
10
+
11
+ def child(name)
12
+ result = @children.select { |child| child.name == name }.first if @children
13
+ result || CookbookDir.new(name, self)
14
+ end
15
+
16
+ def children
17
+ @children ||= rest.get_rest(api_path).map { |key, value| CookbookDir.new(key, self, value) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ require 'chef_fs/file_system/base_fs_dir'
2
+ require 'chef_fs/file_system/rest_list_entry'
3
+
4
+ # TODO: take environment into account
5
+
6
+ module ChefFS
7
+ module FileSystem
8
+ class DataBagDir < RestListDir
9
+ def initialize(name, parent, exists = nil)
10
+ super(name, parent)
11
+ @exists = nil
12
+ end
13
+
14
+ def exists?
15
+ if @exists.nil?
16
+ @exists = parent.children.any? { |child| child.name == name }
17
+ end
18
+ @exists
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ require 'chef_fs/file_system/rest_list_dir'
2
+ require 'chef_fs/file_system/data_bag_dir'
3
+
4
+ # TODO: take environment into account
5
+
6
+ module ChefFS
7
+ module FileSystem
8
+ class DataBagsDir < RestListDir
9
+ def initialize(parent)
10
+ super("data_bags", parent, "data")
11
+ end
12
+
13
+ def child(name)
14
+ result = @children.select { |child| child.name == name }.first if @children
15
+ result || DataBagDir.new(name, self)
16
+ end
17
+
18
+ def children
19
+ @children ||= rest.get_rest(api_path).keys.map { |entry| DataBagDir.new(entry, self, true) }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ require 'chef_fs/file_system/base_fs_dir'
2
+ require 'chef_fs/file_system/rest_list_dir'
3
+ require 'chef_fs/path_utils'
4
+
5
+ module ChefFS
6
+ module FileSystem
7
+ class FileSystemEntry < BaseFSDir
8
+ def initialize(name, parent, file_path = nil)
9
+ super(name, parent)
10
+ @file_path = file_path || "#{parent.file_path}/#{name}"
11
+ end
12
+
13
+ attr_reader :file_path
14
+
15
+ def path_for_printing
16
+ ChefFS::PathUtils::relative_to(file_path, File.absolute_path(Dir.pwd))
17
+ end
18
+
19
+ def children
20
+ @children ||= Dir.entries(file_path).select { |entry| entry[0] != "." }.map { |entry| FileSystemEntry.new(entry, self) }
21
+ end
22
+
23
+ def dir?
24
+ File.directory?(file_path)
25
+ end
26
+
27
+ def read
28
+ begin
29
+ IO.read(file_path)
30
+ rescue IONotFoundException # TODO real exception
31
+ raise NotFoundException, $!
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end