knife-essentials 0.8.2 → 0.8.3
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.
- data/lib/chef/knife/deps_essentials.rb +42 -29
- data/lib/chef/knife/raw_essentials.rb +0 -1
- data/lib/chef_fs/file_system/chef_repository_file_system_entry.rb +10 -8
- data/lib/chef_fs/file_system/chef_repository_file_system_root_dir.rb +26 -1
- data/lib/chef_fs/file_system/file_system_entry.rb +1 -1
- data/lib/chef_fs/file_system/multiplexed_dir.rb +1 -1
- data/lib/chef_fs/knife.rb +11 -6
- data/lib/chef_fs/path_utils.rb +25 -1
- data/lib/chef_fs/version.rb +1 -1
- data/spec/integration/chef_repository_file_system_spec.rb +234 -4
- data/spec/integration/deps_spec.rb +320 -3
- data/spec/integration/list_spec.rb +189 -2
- data/spec/support/integration_helper.rb +28 -0
- data/spec/support/knife_support.rb +12 -0
- metadata +2 -2
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'chef_fs/knife'
|
2
2
|
require 'chef_fs/file_system'
|
3
|
+
require 'chef/run_list'
|
3
4
|
|
4
5
|
class Chef
|
5
6
|
class Knife
|
@@ -71,42 +72,54 @@ class Chef
|
|
71
72
|
|
72
73
|
def get_dependencies(entry)
|
73
74
|
begin
|
74
|
-
|
75
|
-
|
76
|
-
ui.error "#{format_path(entry.path)}: No such file or directory"
|
77
|
-
self.exit_code = 2
|
78
|
-
return []
|
79
|
-
end
|
80
|
-
if !object
|
81
|
-
# If it's not a Chef object, it has no deps
|
82
|
-
return []
|
83
|
-
end
|
75
|
+
if entry.parent && entry.parent.path == '/cookbooks'
|
76
|
+
return entry.chef_object.metadata.dependencies.keys.map { |cookbook| "/cookbooks/#{cookbook}"}
|
84
77
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
78
|
+
elsif entry.parent && entry.parent.path == '/nodes'
|
79
|
+
node = JSON.parse(entry.read, :create_additions => false)
|
80
|
+
result = []
|
81
|
+
if node['chef_environment'] && node['chef_environment'] != '_default'
|
82
|
+
result << "/environments/#{node['chef_environment']}.json"
|
83
|
+
end
|
84
|
+
if node['run_list']
|
85
|
+
result += dependencies_from_runlist(node['run_list'])
|
86
|
+
end
|
87
|
+
result
|
88
|
+
|
89
|
+
elsif entry.parent && entry.parent.path == '/roles'
|
90
|
+
role = JSON.parse(entry.read, :create_additions => false)
|
91
|
+
result = []
|
92
|
+
if role['run_list']
|
93
|
+
dependencies_from_runlist(role['run_list']).each do |dependency|
|
94
|
+
result << dependency if !result.include?(dependency)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
if role['env_run_lists']
|
98
|
+
role['env_run_lists'].each_pair do |env,run_list|
|
99
|
+
dependencies_from_runlist(run_list).each do |dependency|
|
100
|
+
result << dependency if !result.include?(dependency)
|
101
|
+
end
|
102
|
+
end
|
100
103
|
end
|
104
|
+
result
|
105
|
+
|
106
|
+
elsif !entry.exists?
|
107
|
+
raise ChefFS::FileSystem::NotFoundError, "Nonexistent #{entry.path_for_printing}"
|
108
|
+
|
109
|
+
else
|
110
|
+
[]
|
101
111
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
112
|
+
rescue ChefFS::FileSystem::NotFoundError
|
113
|
+
ui.error "#{format_path(entry.path)}: No such file or directory"
|
114
|
+
self.exit_code = 2
|
115
|
+
[]
|
105
116
|
end
|
106
117
|
end
|
107
118
|
|
108
119
|
def dependencies_from_runlist(run_list)
|
109
|
-
|
120
|
+
chef_run_list = Chef::RunList.new
|
121
|
+
chef_run_list.reset!(run_list)
|
122
|
+
chef_run_list.map do |run_list_item|
|
110
123
|
case run_list_item.type
|
111
124
|
when :role
|
112
125
|
"/roles/#{run_list_item.name}.json"
|
@@ -18,19 +18,15 @@
|
|
18
18
|
|
19
19
|
require 'chef_fs/file_system/file_system_entry'
|
20
20
|
require 'chef/cookbook/cookbook_version_loader'
|
21
|
-
require 'chef/node'
|
22
|
-
require 'chef/role'
|
23
|
-
require 'chef/environment'
|
24
|
-
require 'chef/data_bag_item'
|
25
|
-
require 'chef/client'
|
26
21
|
|
27
22
|
module ChefFS
|
28
23
|
module FileSystem
|
29
24
|
# ChefRepositoryFileSystemEntry works just like FileSystemEntry,
|
30
25
|
# except can inflate Chef objects
|
31
26
|
class ChefRepositoryFileSystemEntry < FileSystemEntry
|
32
|
-
def initialize(name, parent, file_path = nil,
|
27
|
+
def initialize(name, parent, file_path = nil, json_class = nil)
|
33
28
|
super(name, parent, file_path)
|
29
|
+
@json_class = json_class
|
34
30
|
end
|
35
31
|
|
36
32
|
def chefignore
|
@@ -41,6 +37,10 @@ module ChefFS
|
|
41
37
|
parent.ignore_empty_directories?
|
42
38
|
end
|
43
39
|
|
40
|
+
def json_class
|
41
|
+
@json_class || parent.json_class
|
42
|
+
end
|
43
|
+
|
44
44
|
def chef_object
|
45
45
|
begin
|
46
46
|
if parent.path == '/cookbooks'
|
@@ -49,8 +49,10 @@ module ChefFS
|
|
49
49
|
return loader.cookbook_version
|
50
50
|
end
|
51
51
|
|
52
|
-
# Otherwise the
|
53
|
-
|
52
|
+
# Otherwise, inflate the file using the chosen JSON class (if any)
|
53
|
+
if json_class
|
54
|
+
return json_class.json_create(JSON.parse(read, :create_additions => false))
|
55
|
+
end
|
54
56
|
rescue
|
55
57
|
Chef::Log.error("Could not read #{path_for_printing} into a Chef object: #{$!}")
|
56
58
|
end
|
@@ -20,6 +20,11 @@ require 'chef_fs/file_system/base_fs_dir'
|
|
20
20
|
require 'chef_fs/file_system/chef_repository_file_system_entry'
|
21
21
|
require 'chef_fs/file_system/chef_repository_file_system_cookbooks_dir'
|
22
22
|
require 'chef_fs/file_system/multiplexed_dir'
|
23
|
+
require 'chef/api_client'
|
24
|
+
require 'chef/data_bag_item'
|
25
|
+
require 'chef/environment'
|
26
|
+
require 'chef/node'
|
27
|
+
require 'chef/role'
|
23
28
|
|
24
29
|
module ChefFS
|
25
30
|
module FileSystem
|
@@ -54,6 +59,10 @@ module ChefFS
|
|
54
59
|
nil
|
55
60
|
end
|
56
61
|
|
62
|
+
def json_class
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
57
66
|
private
|
58
67
|
|
59
68
|
def make_child_entry(name)
|
@@ -66,7 +75,23 @@ module ChefFS
|
|
66
75
|
if name == 'cookbooks'
|
67
76
|
dirs = paths.map { |path| ChefRepositoryFileSystemCookbooksDir.new(name, self, path) }
|
68
77
|
else
|
69
|
-
|
78
|
+
json_class = case name
|
79
|
+
when 'clients'
|
80
|
+
Chef::ApiClient
|
81
|
+
when 'data_bags'
|
82
|
+
Chef::DataBagItem
|
83
|
+
when 'environments'
|
84
|
+
Chef::Environment
|
85
|
+
when 'nodes'
|
86
|
+
Chef::Node
|
87
|
+
when 'roles'
|
88
|
+
Chef::Role
|
89
|
+
when 'users'
|
90
|
+
nil
|
91
|
+
else
|
92
|
+
raise "Unknown top level path #{name}"
|
93
|
+
end
|
94
|
+
dirs = paths.map { |path| ChefRepositoryFileSystemEntry.new(name, self, path, json_class) }
|
70
95
|
end
|
71
96
|
MultiplexedDir.new(dirs)
|
72
97
|
end
|
@@ -23,7 +23,7 @@ module ChefFS
|
|
23
23
|
multiplexed_dirs.each do |dir|
|
24
24
|
dir.children.each do |child|
|
25
25
|
if seen[child.name]
|
26
|
-
Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{child} and #{seen[child.name]}")
|
26
|
+
Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{child.path_for_printing} and #{seen[child.name].path_for_printing}")
|
27
27
|
else
|
28
28
|
result << child
|
29
29
|
seen[child.name] = child
|
data/lib/chef_fs/knife.rb
CHANGED
@@ -112,22 +112,23 @@ module ChefFS
|
|
112
112
|
# If the path does not reach into ANY specified directory, nil is returned.
|
113
113
|
def server_path(file_path)
|
114
114
|
pwd = File.expand_path(Dir.pwd)
|
115
|
-
absolute_path = File.expand_path(file_path, pwd)
|
115
|
+
absolute_path = ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd))
|
116
116
|
|
117
117
|
# Check all object paths (cookbooks_dir, data_bags_dir, etc.)
|
118
118
|
object_paths.each_pair do |name, paths|
|
119
119
|
paths.each do |path|
|
120
|
-
|
121
|
-
|
120
|
+
realest_path = ChefFS::PathUtils.realest_path(path)
|
121
|
+
if absolute_path[0,realest_path.length] == realest_path
|
122
|
+
relative_path = ChefFS::PathUtils::relative_to(absolute_path, realest_path)
|
122
123
|
return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}"
|
123
124
|
end
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
127
128
|
# Check chef_repo_path
|
128
|
-
|
129
|
-
|
130
|
-
return
|
129
|
+
realest_chef_repo_path = ChefFS::PathUtils.realest_path(chef_repo_path)
|
130
|
+
if absolute_path == realest_chef_repo_path
|
131
|
+
return '/'
|
131
132
|
end
|
132
133
|
|
133
134
|
nil
|
@@ -164,6 +165,10 @@ module ChefFS
|
|
164
165
|
# TODO support absolute file paths and not just patterns? Too much?
|
165
166
|
# Could be super useful in a world with multiple repo paths
|
166
167
|
args.map do |arg|
|
168
|
+
if !base_path && !PathUtils.is_absolute?(arg)
|
169
|
+
ui.error("Attempt to use relative path '#{arg}' when current directory is outside the repository path")
|
170
|
+
exit(1)
|
171
|
+
end
|
167
172
|
ChefFS::FilePattern::relative_to(base_path, arg)
|
168
173
|
end
|
169
174
|
end
|
data/lib/chef_fs/path_utils.rb
CHANGED
@@ -29,7 +29,7 @@ module ChefFS
|
|
29
29
|
source_parts = ChefFS::PathUtils.split(source)
|
30
30
|
dest_parts = ChefFS::PathUtils.split(dest)
|
31
31
|
i = 0
|
32
|
-
until i >= source_parts.length || i >= dest_parts.length || source_parts[i] !=
|
32
|
+
until i >= source_parts.length || i >= dest_parts.length || source_parts[i] != dest_parts[i]
|
33
33
|
i+=1
|
34
34
|
end
|
35
35
|
# dot-dot up from 'source' to the common ancestor, then
|
@@ -58,5 +58,29 @@ module ChefFS
|
|
58
58
|
ChefFS::windows? ? '[/\\]' : '/'
|
59
59
|
end
|
60
60
|
|
61
|
+
# Given a path which may only be partly real (i.e. /x/y/z when only /x exists,
|
62
|
+
# or /x/y/*/blah when /x/y/z/blah exists), call File.realpath on the biggest
|
63
|
+
# part that actually exists.
|
64
|
+
#
|
65
|
+
# If /x is a symlink to /blarghle, and has no subdirectories, then:
|
66
|
+
# PathUtils.realest_path('/x/y/z') == '/blarghle/y/z'
|
67
|
+
# PathUtils.realest_path('/x/*/z') == '/blarghle/*/z'
|
68
|
+
# PathUtils.realest_path('/*/y/z') == '/*/y/z'
|
69
|
+
def self.realest_path(path)
|
70
|
+
begin
|
71
|
+
File.realpath(path)
|
72
|
+
rescue Errno::ENOENT
|
73
|
+
dirname = File.dirname(path)
|
74
|
+
if dirname
|
75
|
+
PathUtils.join(realest_path(dirname), File.basename(path))
|
76
|
+
else
|
77
|
+
path
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.is_absolute?(path)
|
83
|
+
path =~ /^#{regexp_path_separator}/
|
84
|
+
end
|
61
85
|
end
|
62
86
|
end
|
data/lib/chef_fs/version.rb
CHANGED
@@ -597,7 +597,7 @@ EOM
|
|
597
597
|
file 'cookbooks1/chefignore', "metadata.rb\n"
|
598
598
|
file 'cookbooks2/chefignore', "x.json\n"
|
599
599
|
it "chefignores apply only to the directories they are in" do
|
600
|
-
knife('list --local -R /').should_succeed
|
600
|
+
knife('list --local -R /').should_succeed(<<EOM, :stderr => "WARN: Child with name 'chefignore' found in multiple directories: #{Chef::Config.chef_repo_path}/cookbooks2/chefignore and #{Chef::Config.chef_repo_path}/cookbooks1/chefignore\n")
|
601
601
|
/:
|
602
602
|
cookbooks
|
603
603
|
|
@@ -621,7 +621,7 @@ EOM
|
|
621
621
|
file 'cookbooks2/yourcookbook/onlyincookbooks2.rb', ''
|
622
622
|
|
623
623
|
it "chefignores apply only to the winning cookbook" do
|
624
|
-
knife('list --local -R /').should_succeed
|
624
|
+
knife('list --local -R /').should_succeed(<<EOM, :stderr => "WARN: Child with name 'chefignore' found in multiple directories: #{Chef::Config.chef_repo_path}/cookbooks2/chefignore and #{Chef::Config.chef_repo_path}/cookbooks1/chefignore\nWARN: Child with name 'yourcookbook' found in multiple directories: #{Chef::Config.chef_repo_path}/cookbooks2/yourcookbook and #{Chef::Config.chef_repo_path}/cookbooks1/yourcookbook\n")
|
625
625
|
/:
|
626
626
|
cookbooks
|
627
627
|
|
@@ -644,7 +644,237 @@ EOM
|
|
644
644
|
end
|
645
645
|
|
646
646
|
# TODO alternate repo_path / *_path
|
647
|
-
|
647
|
+
context 'alternate *_path' do
|
648
|
+
when_the_repository 'has clients and clients2, cookbooks and cookbooks2, etc.' do
|
649
|
+
file 'clients/client1.json', {}
|
650
|
+
file 'cookbooks/cookbook1/metadata.rb', ''
|
651
|
+
file 'data_bags/bag/item.json', {}
|
652
|
+
file 'environments/env1.json', {}
|
653
|
+
file 'nodes/node1.json', {}
|
654
|
+
file 'roles/role1.json', {}
|
655
|
+
file 'users/user1.json', {}
|
656
|
+
|
657
|
+
file 'clients2/client1.json', {}
|
658
|
+
file 'cookbooks2/cookbook2/metadata.rb', ''
|
659
|
+
file 'data_bags2/bag2/item2.json', {}
|
660
|
+
file 'environments2/env2.json', {}
|
661
|
+
file 'nodes2/node2.json', {}
|
662
|
+
file 'roles2/role2.json', {}
|
663
|
+
file 'users2/user2.json', {}
|
664
|
+
|
665
|
+
directory 'chef_repo2' do
|
666
|
+
file 'clients/client3.json', {}
|
667
|
+
file 'cookbooks/cookbook3/metadata.rb', ''
|
668
|
+
file 'data_bags/bag3/item3.json', {}
|
669
|
+
file 'environments/env3.json', {}
|
670
|
+
file 'nodes/node3.json', {}
|
671
|
+
file 'roles/role3.json', {}
|
672
|
+
file 'users/user3.json', {}
|
673
|
+
end
|
674
|
+
|
675
|
+
context 'when all _paths are set to alternates' do
|
676
|
+
before :each do
|
677
|
+
%w(client cookbook data_bag environment node role user).each do |object_name|
|
678
|
+
Chef::Config["#{object_name}_path".to_sym] = File.join(Chef::Config.chef_repo_path, "#{object_name}s2")
|
679
|
+
end
|
680
|
+
Chef::Config.chef_repo_path = File.join(Chef::Config.chef_repo_path, 'chef_repo2')
|
681
|
+
end
|
682
|
+
|
683
|
+
context 'when cwd is at the top level' do
|
684
|
+
cwd '.'
|
685
|
+
it 'knife list --local -R fails' do
|
686
|
+
knife('list --local -R').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
690
|
+
context 'when cwd is inside the data_bags directory' do
|
691
|
+
cwd 'data_bags'
|
692
|
+
it 'knife list --local -R fails' do
|
693
|
+
knife('list --local -R').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
context 'when cwd is inside chef_repo2' do
|
698
|
+
cwd 'chef_repo2'
|
699
|
+
it 'knife list --local -R lists everything' do
|
700
|
+
knife('list --local -R').should_succeed <<EOM
|
701
|
+
.:
|
702
|
+
cookbooks
|
703
|
+
data_bags
|
704
|
+
environments
|
705
|
+
roles
|
706
|
+
|
707
|
+
cookbooks:
|
708
|
+
cookbook2
|
709
|
+
|
710
|
+
cookbooks/cookbook2:
|
711
|
+
metadata.rb
|
712
|
+
|
713
|
+
data_bags:
|
714
|
+
bag2
|
715
|
+
|
716
|
+
data_bags/bag2:
|
717
|
+
item2.json
|
718
|
+
|
719
|
+
environments:
|
720
|
+
env2.json
|
721
|
+
|
722
|
+
roles:
|
723
|
+
role2.json
|
724
|
+
EOM
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
context 'when cwd is inside data_bags2' do
|
729
|
+
cwd 'data_bags2'
|
730
|
+
it 'knife list --local -R lists data bags' do
|
731
|
+
knife('list --local -R').should_succeed <<EOM
|
732
|
+
.:
|
733
|
+
bag2
|
734
|
+
|
735
|
+
bag2:
|
736
|
+
item2.json
|
737
|
+
EOM
|
738
|
+
end
|
739
|
+
it 'knife list --local -R ../roles lists roles' do
|
740
|
+
knife('list --local -R ../roles').should_succeed "/roles/role2.json\n"
|
741
|
+
end
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
context 'when all _paths except chef_repo_path are set to alternates' do
|
746
|
+
before :each do
|
747
|
+
%w(client cookbook data_bag environment node role user).each do |object_name|
|
748
|
+
Chef::Config["#{object_name}_path".to_sym] = File.join(Chef::Config.chef_repo_path, "#{object_name}s2")
|
749
|
+
end
|
750
|
+
end
|
751
|
+
|
752
|
+
context 'when cwd is at the top level' do
|
753
|
+
cwd '.'
|
754
|
+
it 'knife list --local -R lists everything' do
|
755
|
+
knife('list --local -R').should_succeed <<EOM
|
756
|
+
.:
|
757
|
+
cookbooks
|
758
|
+
data_bags
|
759
|
+
environments
|
760
|
+
roles
|
761
|
+
|
762
|
+
cookbooks:
|
763
|
+
cookbook2
|
764
|
+
|
765
|
+
cookbooks/cookbook2:
|
766
|
+
metadata.rb
|
767
|
+
|
768
|
+
data_bags:
|
769
|
+
bag2
|
770
|
+
|
771
|
+
data_bags/bag2:
|
772
|
+
item2.json
|
773
|
+
|
774
|
+
environments:
|
775
|
+
env2.json
|
776
|
+
|
777
|
+
roles:
|
778
|
+
role2.json
|
779
|
+
EOM
|
780
|
+
end
|
781
|
+
end
|
782
|
+
|
783
|
+
context 'when cwd is inside the data_bags directory' do
|
784
|
+
cwd 'data_bags'
|
785
|
+
it 'knife list --local -R fails' do
|
786
|
+
knife('list --local -R').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
context 'when cwd is inside chef_repo2' do
|
791
|
+
cwd 'chef_repo2'
|
792
|
+
it 'knife list -R fails' do
|
793
|
+
knife('list --local -R').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
context 'when cwd is inside data_bags2' do
|
798
|
+
cwd 'data_bags2'
|
799
|
+
it 'knife list --local -R lists data bags' do
|
800
|
+
knife('list --local -R').should_succeed <<EOM
|
801
|
+
.:
|
802
|
+
bag2
|
803
|
+
|
804
|
+
bag2:
|
805
|
+
item2.json
|
806
|
+
EOM
|
807
|
+
end
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
811
|
+
context 'when only chef_repo_path is set to its alternate' do
|
812
|
+
before :each do
|
813
|
+
%w(client cookbook data_bag environment node role user).each do |object_name|
|
814
|
+
Chef::Config["#{object_name}_path".to_sym] = nil
|
815
|
+
end
|
816
|
+
Chef::Config.chef_repo_path = File.join(Chef::Config.chef_repo_path, 'chef_repo2')
|
817
|
+
end
|
818
|
+
|
819
|
+
context 'when cwd is at the top level' do
|
820
|
+
cwd '.'
|
821
|
+
it 'knife list --local -R fails' do
|
822
|
+
knife('list --local -R').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
context 'when cwd is inside the data_bags directory' do
|
827
|
+
cwd 'data_bags'
|
828
|
+
it 'knife list --local -R fails' do
|
829
|
+
knife('list --local -R').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
|
830
|
+
end
|
831
|
+
end
|
832
|
+
|
833
|
+
context 'when cwd is inside chef_repo2' do
|
834
|
+
cwd 'chef_repo2'
|
835
|
+
it 'knife list --local -R lists everything' do
|
836
|
+
knife('list --local -R').should_succeed <<EOM
|
837
|
+
.:
|
838
|
+
cookbooks
|
839
|
+
data_bags
|
840
|
+
environments
|
841
|
+
roles
|
842
|
+
|
843
|
+
cookbooks:
|
844
|
+
cookbook3
|
845
|
+
|
846
|
+
cookbooks/cookbook3:
|
847
|
+
metadata.rb
|
848
|
+
|
849
|
+
data_bags:
|
850
|
+
bag3
|
851
|
+
|
852
|
+
data_bags/bag3:
|
853
|
+
item3.json
|
854
|
+
|
855
|
+
environments:
|
856
|
+
env3.json
|
857
|
+
|
858
|
+
roles:
|
859
|
+
role3.json
|
860
|
+
EOM
|
861
|
+
end
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
context 'when paths are set to point to both versions of each' do
|
866
|
+
before :each do
|
867
|
+
%w(client cookbooks data_bag environment node role user).each do |object_name|
|
868
|
+
Chef::Config["#{object_name}_path".to_sym] = [
|
869
|
+
File.join(Chef::Config.chef_repo_path, "#{object_name}s"),
|
870
|
+
File.join(Chef::Config.chef_repo_path, "#{object_name}s2")
|
871
|
+
]
|
872
|
+
end
|
873
|
+
Chef::Config.chef_repo_path = File.join(Chef::Config.chef_repo_path, 'chef_repo_top')
|
874
|
+
end
|
875
|
+
end
|
876
|
+
end
|
877
|
+
end
|
878
|
+
|
648
879
|
# TODO nonexistent repo_path / *_path
|
649
|
-
# TODO empty *_path
|
650
880
|
end
|
@@ -5,6 +5,323 @@ describe 'knife deps' do
|
|
5
5
|
extend IntegrationSupport
|
6
6
|
include KnifeSupport
|
7
7
|
|
8
|
+
context 'local' do
|
9
|
+
when_the_repository 'has a role with no run_list' do
|
10
|
+
file 'roles/starring.json', {}
|
11
|
+
it 'knife deps reports no dependencies' do
|
12
|
+
knife('deps /roles/starring.json').should_succeed "/roles/starring.json\n"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
when_the_repository 'has a role with a default run_list' do
|
17
|
+
file 'roles/starring.json', { 'run_list' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) }
|
18
|
+
file 'roles/minor.json', {}
|
19
|
+
file 'cookbooks/quiche/metadata.rb', ''
|
20
|
+
file 'cookbooks/quiche/recipes/default.rb', ''
|
21
|
+
file 'cookbooks/soup/metadata.rb', ''
|
22
|
+
file 'cookbooks/soup/recipes/chicken.rb', ''
|
23
|
+
it 'knife deps reports all dependencies' do
|
24
|
+
knife('deps /roles/starring.json').should_succeed <<EOM
|
25
|
+
/roles/minor.json
|
26
|
+
/cookbooks/quiche
|
27
|
+
/cookbooks/soup
|
28
|
+
/roles/starring.json
|
29
|
+
EOM
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
when_the_repository 'has a role with an env_run_list' do
|
34
|
+
file 'roles/starring.json', { 'env_run_lists' => { 'desert' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) } }
|
35
|
+
file 'roles/minor.json', {}
|
36
|
+
file 'cookbooks/quiche/metadata.rb', ''
|
37
|
+
file 'cookbooks/quiche/recipes/default.rb', ''
|
38
|
+
file 'cookbooks/soup/metadata.rb', ''
|
39
|
+
file 'cookbooks/soup/recipes/chicken.rb', ''
|
40
|
+
it 'knife deps reports all dependencies' do
|
41
|
+
knife('deps /roles/starring.json').should_succeed <<EOM
|
42
|
+
/roles/minor.json
|
43
|
+
/cookbooks/quiche
|
44
|
+
/cookbooks/soup
|
45
|
+
/roles/starring.json
|
46
|
+
EOM
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
when_the_repository 'has a node with no environment or run_list' do
|
51
|
+
file 'nodes/mort.json', {}
|
52
|
+
it 'knife deps reports just the node' do
|
53
|
+
knife('deps --repo-mode=everything /nodes/mort.json').should_succeed "/nodes/mort.json\n"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
when_the_repository 'has a node with an environment' do
|
57
|
+
file 'environments/desert.json', {}
|
58
|
+
file 'nodes/mort.json', { 'chef_environment' => 'desert' }
|
59
|
+
it 'knife deps reports just the node' do
|
60
|
+
knife('deps --repo-mode=everything /nodes/mort.json').should_succeed "/environments/desert.json\n/nodes/mort.json\n"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
when_the_repository 'has a node with roles and recipes in its run_list' do
|
64
|
+
file 'roles/minor.json', {}
|
65
|
+
file 'cookbooks/quiche/metadata.rb', ''
|
66
|
+
file 'cookbooks/quiche/recipes/default.rb', ''
|
67
|
+
file 'cookbooks/soup/metadata.rb', ''
|
68
|
+
file 'cookbooks/soup/recipes/chicken.rb', ''
|
69
|
+
file 'nodes/mort.json', { 'run_list' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) }
|
70
|
+
it 'knife deps reports just the node' do
|
71
|
+
knife('deps --repo-mode=everything /nodes/mort.json').should_succeed <<EOM
|
72
|
+
/roles/minor.json
|
73
|
+
/cookbooks/quiche
|
74
|
+
/cookbooks/soup
|
75
|
+
/nodes/mort.json
|
76
|
+
EOM
|
77
|
+
end
|
78
|
+
end
|
79
|
+
when_the_repository 'has a cookbook with no dependencies' do
|
80
|
+
file 'cookbooks/quiche/metadata.rb', ''
|
81
|
+
file 'cookbooks/quiche/recipes/default.rb', ''
|
82
|
+
it 'knife deps reports just the cookbook' do
|
83
|
+
knife('deps /cookbooks/quiche').should_succeed "/cookbooks/quiche\n"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
when_the_repository 'has a cookbook with dependencies' do
|
87
|
+
file 'cookbooks/kettle/metadata.rb', ''
|
88
|
+
file 'cookbooks/quiche/metadata.rb', 'depends "kettle"'
|
89
|
+
file 'cookbooks/quiche/recipes/default.rb', ''
|
90
|
+
it 'knife deps reports just the cookbook' do
|
91
|
+
knife('deps /cookbooks/quiche').should_succeed "/cookbooks/kettle\n/cookbooks/quiche\n"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
when_the_repository 'has a data bag' do
|
95
|
+
file 'data_bags/bag/item.json', {}
|
96
|
+
it 'knife deps reports just the data bag' do
|
97
|
+
knife('deps /data_bags/bag/item.json').should_succeed "/data_bags/bag/item.json\n"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
when_the_repository 'has an environment' do
|
101
|
+
file 'environments/desert.json', {}
|
102
|
+
it 'knife deps reports just the environment' do
|
103
|
+
knife('deps /environments/desert.json').should_succeed "/environments/desert.json\n"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
when_the_repository 'has a deep dependency tree' do
|
107
|
+
file 'roles/starring.json', { 'run_list' => %w(role[minor] recipe[quiche] recipe[soup::chicken]) }
|
108
|
+
file 'roles/minor.json', {}
|
109
|
+
file 'cookbooks/quiche/metadata.rb', ''
|
110
|
+
file 'cookbooks/quiche/recipes/default.rb', ''
|
111
|
+
file 'cookbooks/soup/metadata.rb', ''
|
112
|
+
file 'cookbooks/soup/recipes/chicken.rb', ''
|
113
|
+
file 'environments/desert.json', {}
|
114
|
+
file 'nodes/mort.json', { 'chef_environment' => 'desert', 'run_list' => [ 'role[starring]' ] }
|
115
|
+
file 'nodes/bart.json', { 'run_list' => [ 'role[minor]' ] }
|
116
|
+
|
117
|
+
it 'knife deps reports all dependencies' do
|
118
|
+
knife('deps --repo-mode=everything /nodes/mort.json').should_succeed <<EOM
|
119
|
+
/environments/desert.json
|
120
|
+
/roles/minor.json
|
121
|
+
/cookbooks/quiche
|
122
|
+
/cookbooks/soup
|
123
|
+
/roles/starring.json
|
124
|
+
/nodes/mort.json
|
125
|
+
EOM
|
126
|
+
end
|
127
|
+
it 'knife deps * reports all dependencies of all things' do
|
128
|
+
knife('deps --repo-mode=everything /nodes/*').should_succeed <<EOM
|
129
|
+
/roles/minor.json
|
130
|
+
/nodes/bart.json
|
131
|
+
/environments/desert.json
|
132
|
+
/cookbooks/quiche
|
133
|
+
/cookbooks/soup
|
134
|
+
/roles/starring.json
|
135
|
+
/nodes/mort.json
|
136
|
+
EOM
|
137
|
+
end
|
138
|
+
it 'knife deps a b reports all dependencies of a and b' do
|
139
|
+
knife('deps --repo-mode=everything /nodes/bart.json /nodes/mort.json').should_succeed <<EOM
|
140
|
+
/roles/minor.json
|
141
|
+
/nodes/bart.json
|
142
|
+
/environments/desert.json
|
143
|
+
/cookbooks/quiche
|
144
|
+
/cookbooks/soup
|
145
|
+
/roles/starring.json
|
146
|
+
/nodes/mort.json
|
147
|
+
EOM
|
148
|
+
end
|
149
|
+
it 'knife deps --tree /* shows dependencies in a tree' do
|
150
|
+
knife('deps --tree --repo-mode=everything /nodes/*').should_succeed <<EOM
|
151
|
+
/nodes/bart.json
|
152
|
+
/roles/minor.json
|
153
|
+
/nodes/mort.json
|
154
|
+
/environments/desert.json
|
155
|
+
/roles/starring.json
|
156
|
+
/roles/minor.json
|
157
|
+
/cookbooks/quiche
|
158
|
+
/cookbooks/soup
|
159
|
+
EOM
|
160
|
+
end
|
161
|
+
it 'knife deps --tree --no-recurse shows only the first level of dependencies' do
|
162
|
+
knife('deps --tree --no-recurse --repo-mode=everything /nodes/*').should_succeed <<EOM
|
163
|
+
/nodes/bart.json
|
164
|
+
/roles/minor.json
|
165
|
+
/nodes/mort.json
|
166
|
+
/environments/desert.json
|
167
|
+
/roles/starring.json
|
168
|
+
EOM
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'circular dependencies' do
|
173
|
+
when_the_repository 'has cookbooks with circular dependencies' do
|
174
|
+
file 'cookbooks/foo/metadata.rb', 'depends "bar"'
|
175
|
+
file 'cookbooks/bar/metadata.rb', 'depends "baz"'
|
176
|
+
file 'cookbooks/baz/metadata.rb', 'depends "foo"'
|
177
|
+
file 'cookbooks/self/metadata.rb', 'depends "self"'
|
178
|
+
it 'knife deps prints each once' do
|
179
|
+
knife('deps /cookbooks/foo /cookbooks/self').should_succeed <<EOM
|
180
|
+
/cookbooks/baz
|
181
|
+
/cookbooks/bar
|
182
|
+
/cookbooks/foo
|
183
|
+
/cookbooks/self
|
184
|
+
EOM
|
185
|
+
end
|
186
|
+
it 'knife deps --tree prints each once' do
|
187
|
+
knife('deps --tree /cookbooks/foo /cookbooks/self').should_succeed <<EOM
|
188
|
+
/cookbooks/foo
|
189
|
+
/cookbooks/bar
|
190
|
+
/cookbooks/baz
|
191
|
+
/cookbooks/foo
|
192
|
+
/cookbooks/self
|
193
|
+
/cookbooks/self
|
194
|
+
EOM
|
195
|
+
end
|
196
|
+
end
|
197
|
+
when_the_repository 'has roles with circular dependencies' do
|
198
|
+
file 'roles/foo.json', { 'run_list' => [ 'role[bar]' ] }
|
199
|
+
file 'roles/bar.json', { 'run_list' => [ 'role[baz]' ] }
|
200
|
+
file 'roles/baz.json', { 'run_list' => [ 'role[foo]' ] }
|
201
|
+
file 'roles/self.json', { 'run_list' => [ 'role[self]' ] }
|
202
|
+
it 'knife deps prints each once' do
|
203
|
+
knife('deps /roles/foo.json /roles/self.json').should_succeed <<EOM
|
204
|
+
/roles/baz.json
|
205
|
+
/roles/bar.json
|
206
|
+
/roles/foo.json
|
207
|
+
/roles/self.json
|
208
|
+
EOM
|
209
|
+
end
|
210
|
+
it 'knife deps --tree prints each once' do
|
211
|
+
knife('deps --tree /roles/foo.json /roles/self.json') do
|
212
|
+
stdout.should == "/roles/foo.json\n /roles/bar.json\n /roles/baz.json\n /roles/foo.json\n/roles/self.json\n /roles/self.json\n"
|
213
|
+
stderr.should == "WARNING: No knife configuration file found\n"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'missing objects' do
|
220
|
+
when_the_repository 'is empty' do
|
221
|
+
it 'knife deps /blah reports an error' do
|
222
|
+
knife('deps /blah').should_fail(
|
223
|
+
:exit_code => 2,
|
224
|
+
:stdout => "/blah\n",
|
225
|
+
:stderr => "ERROR: /blah: No such file or directory\n"
|
226
|
+
)
|
227
|
+
end
|
228
|
+
it 'knife deps /roles/x.json reports an error' do
|
229
|
+
knife('deps /roles/x.json').should_fail(
|
230
|
+
:exit_code => 2,
|
231
|
+
:stdout => "/roles/x.json\n",
|
232
|
+
:stderr => "ERROR: /roles/x.json: No such file or directory\n"
|
233
|
+
)
|
234
|
+
end
|
235
|
+
it 'knife deps /nodes/x.json reports an error' do
|
236
|
+
knife('deps --repo-mode=everything /nodes/x.json').should_fail(
|
237
|
+
:exit_code => 2,
|
238
|
+
:stdout => "/nodes/x.json\n",
|
239
|
+
:stderr => "ERROR: /nodes/x.json: No such file or directory\n"
|
240
|
+
)
|
241
|
+
end
|
242
|
+
it 'knife deps /environments/x.json reports an error' do
|
243
|
+
knife('deps /environments/x.json').should_fail(
|
244
|
+
:exit_code => 2,
|
245
|
+
:stdout => "/environments/x.json\n",
|
246
|
+
:stderr => "ERROR: /environments/x.json: No such file or directory\n"
|
247
|
+
)
|
248
|
+
end
|
249
|
+
it 'knife deps /cookbooks/x reports an error' do
|
250
|
+
knife('deps /cookbooks/x').should_fail(
|
251
|
+
:exit_code => 2,
|
252
|
+
:stdout => "/cookbooks/x\n",
|
253
|
+
:stderr => "ERROR: /cookbooks/x: No such file or directory\n"
|
254
|
+
)
|
255
|
+
end
|
256
|
+
it 'knife deps /data_bags/bag/item reports an error' do
|
257
|
+
knife('deps /data_bags/bag/item').should_fail(
|
258
|
+
:exit_code => 2,
|
259
|
+
:stdout => "/data_bags/bag/item\n",
|
260
|
+
:stderr => "ERROR: /data_bags/bag/item: No such file or directory\n"
|
261
|
+
)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
when_the_repository 'is missing a dependent cookbook' do
|
265
|
+
file 'roles/starring.json', { 'run_list' => [ 'recipe[quiche]'] }
|
266
|
+
it 'knife deps reports the cookbook, along with an error' do
|
267
|
+
knife('deps /roles/starring.json').should_fail(
|
268
|
+
:exit_code => 2,
|
269
|
+
:stdout => "/cookbooks/quiche\n/roles/starring.json\n",
|
270
|
+
:stderr => "ERROR: /cookbooks/quiche: No such file or directory\n"
|
271
|
+
)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
when_the_repository 'is missing a dependent environment' do
|
275
|
+
file 'nodes/mort.json', { 'chef_environment' => 'desert' }
|
276
|
+
it 'knife deps reports the environment, along with an error' do
|
277
|
+
knife('deps --repo-mode=everything /nodes/mort.json').should_fail(
|
278
|
+
:exit_code => 2,
|
279
|
+
:stdout => "/environments/desert.json\n/nodes/mort.json\n",
|
280
|
+
:stderr => "ERROR: /environments/desert.json: No such file or directory\n"
|
281
|
+
)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
when_the_repository 'is missing a dependent role' do
|
285
|
+
file 'roles/starring.json', { 'run_list' => [ 'role[minor]'] }
|
286
|
+
it 'knife deps reports the role, along with an error' do
|
287
|
+
knife('deps /roles/starring.json').should_fail(
|
288
|
+
:exit_code => 2,
|
289
|
+
:stdout => "/roles/minor.json\n/roles/starring.json\n",
|
290
|
+
:stderr => "ERROR: /roles/minor.json: No such file or directory\n"
|
291
|
+
)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
context 'invalid objects' do
|
296
|
+
when_the_repository 'is empty' do
|
297
|
+
it 'knife deps / reports itself only' do
|
298
|
+
knife('deps /').should_succeed("/\n")
|
299
|
+
end
|
300
|
+
it 'knife deps /roles reports an error' do
|
301
|
+
knife('deps /roles').should_fail(
|
302
|
+
:exit_code => 2,
|
303
|
+
:stderr => "ERROR: /roles: No such file or directory\n",
|
304
|
+
:stdout => "/roles\n"
|
305
|
+
)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
when_the_repository 'has a data bag' do
|
309
|
+
file 'data_bags/bag/item.json', ''
|
310
|
+
it 'knife deps /data_bags/bag shows no dependencies' do
|
311
|
+
knife('deps /data_bags/bag').should_succeed("/data_bags/bag\n")
|
312
|
+
end
|
313
|
+
end
|
314
|
+
when_the_repository 'has a cookbook' do
|
315
|
+
file 'cookbooks/blah/metadata.rb', ''
|
316
|
+
it 'knife deps on a cookbook file shows no dependencies' do
|
317
|
+
knife('deps /cookbooks/blah/metadata.rb').should_succeed(
|
318
|
+
"/cookbooks/blah/metadata.rb\n"
|
319
|
+
)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
8
325
|
context 'remote' do
|
9
326
|
when_the_chef_server 'has a role with no run_list' do
|
10
327
|
role 'starring', {}
|
@@ -306,9 +623,9 @@ EOM
|
|
306
623
|
end
|
307
624
|
end
|
308
625
|
end
|
626
|
+
end
|
309
627
|
|
310
|
-
|
311
|
-
|
312
|
-
end
|
628
|
+
it 'knife deps --no-recurse reports an error' do
|
629
|
+
knife('deps --no-recurse /').should_fail("ERROR: --no-recurse requires --tree\n")
|
313
630
|
end
|
314
631
|
end
|
@@ -225,7 +225,195 @@ EOM
|
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
-
|
228
|
+
context 'symlink tests' do
|
229
|
+
when_the_repository 'is empty' do
|
230
|
+
context 'when cwd is at the top of the repository' do
|
231
|
+
cwd '.'
|
232
|
+
|
233
|
+
it "knife list -Rp --flat returns everything" do
|
234
|
+
knife('list -Rp --flat').should_succeed <<EOM
|
235
|
+
cookbooks/
|
236
|
+
cookbooks/cookbook1/
|
237
|
+
cookbooks/cookbook1/metadata.rb
|
238
|
+
cookbooks/cookbook2/
|
239
|
+
cookbooks/cookbook2/metadata.rb
|
240
|
+
cookbooks/cookbook2/recipes/
|
241
|
+
cookbooks/cookbook2/recipes/default.rb
|
242
|
+
data_bags/
|
243
|
+
data_bags/bag1/
|
244
|
+
data_bags/bag1/item1.json
|
245
|
+
data_bags/bag1/item2.json
|
246
|
+
data_bags/bag2/
|
247
|
+
data_bags/bag2/item1.json
|
248
|
+
data_bags/bag2/item2.json
|
249
|
+
environments/
|
250
|
+
environments/_default.json
|
251
|
+
environments/environment1.json
|
252
|
+
environments/environment2.json
|
253
|
+
roles/
|
254
|
+
roles/role1.json
|
255
|
+
roles/role2.json
|
256
|
+
EOM
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
when_the_repository 'has a cookbooks directory' do
|
262
|
+
directory 'cookbooks'
|
263
|
+
context 'when cwd is in cookbooks/' do
|
264
|
+
cwd 'cookbooks'
|
265
|
+
|
266
|
+
it "knife list -Rp --flat / returns everything" do
|
267
|
+
knife('list -Rp --flat /').should_succeed <<EOM
|
268
|
+
./
|
269
|
+
cookbook1/
|
270
|
+
cookbook1/metadata.rb
|
271
|
+
cookbook2/
|
272
|
+
cookbook2/metadata.rb
|
273
|
+
cookbook2/recipes/
|
274
|
+
cookbook2/recipes/default.rb
|
275
|
+
/data_bags/
|
276
|
+
/data_bags/bag1/
|
277
|
+
/data_bags/bag1/item1.json
|
278
|
+
/data_bags/bag1/item2.json
|
279
|
+
/data_bags/bag2/
|
280
|
+
/data_bags/bag2/item1.json
|
281
|
+
/data_bags/bag2/item2.json
|
282
|
+
/environments/
|
283
|
+
/environments/_default.json
|
284
|
+
/environments/environment1.json
|
285
|
+
/environments/environment2.json
|
286
|
+
/roles/
|
287
|
+
/roles/role1.json
|
288
|
+
/roles/role2.json
|
289
|
+
EOM
|
290
|
+
end
|
291
|
+
|
292
|
+
it "knife list -Rp --flat .. returns everything" do
|
293
|
+
knife('list -Rp --flat ..').should_succeed <<EOM
|
294
|
+
./
|
295
|
+
cookbook1/
|
296
|
+
cookbook1/metadata.rb
|
297
|
+
cookbook2/
|
298
|
+
cookbook2/metadata.rb
|
299
|
+
cookbook2/recipes/
|
300
|
+
cookbook2/recipes/default.rb
|
301
|
+
/data_bags/
|
302
|
+
/data_bags/bag1/
|
303
|
+
/data_bags/bag1/item1.json
|
304
|
+
/data_bags/bag1/item2.json
|
305
|
+
/data_bags/bag2/
|
306
|
+
/data_bags/bag2/item1.json
|
307
|
+
/data_bags/bag2/item2.json
|
308
|
+
/environments/
|
309
|
+
/environments/_default.json
|
310
|
+
/environments/environment1.json
|
311
|
+
/environments/environment2.json
|
312
|
+
/roles/
|
313
|
+
/roles/role1.json
|
314
|
+
/roles/role2.json
|
315
|
+
EOM
|
316
|
+
end
|
317
|
+
|
318
|
+
it "knife list -Rp --flat returns cookbooks" do
|
319
|
+
knife('list -Rp --flat').should_succeed <<EOM
|
320
|
+
cookbook1/
|
321
|
+
cookbook1/metadata.rb
|
322
|
+
cookbook2/
|
323
|
+
cookbook2/metadata.rb
|
324
|
+
cookbook2/recipes/
|
325
|
+
cookbook2/recipes/default.rb
|
326
|
+
EOM
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
when_the_repository 'has a cookbooks/cookbook2 directory' do
|
332
|
+
directory 'cookbooks/cookbook2'
|
333
|
+
|
334
|
+
context 'when cwd is in cookbooks/cookbook2' do
|
335
|
+
cwd 'cookbooks/cookbook2'
|
336
|
+
|
337
|
+
it "knife list -Rp --flat returns cookbooks" do
|
338
|
+
knife('list -Rp --flat').should_succeed <<EOM
|
339
|
+
metadata.rb
|
340
|
+
recipes/
|
341
|
+
recipes/default.rb
|
342
|
+
EOM
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
when_the_repository 'has a cookbooks directory and a symlinked cookbooks directory' do
|
348
|
+
directory 'cookbooks'
|
349
|
+
symlink 'symlinked', 'cookbooks'
|
350
|
+
|
351
|
+
context 'when cwd is in cookbooks/' do
|
352
|
+
cwd 'cookbooks'
|
353
|
+
|
354
|
+
it "knife list -Rp --flat returns cookbooks" do
|
355
|
+
knife('list -Rp --flat').should_succeed <<EOM
|
356
|
+
cookbook1/
|
357
|
+
cookbook1/metadata.rb
|
358
|
+
cookbook2/
|
359
|
+
cookbook2/metadata.rb
|
360
|
+
cookbook2/recipes/
|
361
|
+
cookbook2/recipes/default.rb
|
362
|
+
EOM
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
context 'when cwd is in symlinked/' do
|
367
|
+
cwd 'symlinked'
|
368
|
+
|
369
|
+
it "knife list -Rp --flat returns cookbooks" do
|
370
|
+
knife('list -Rp --flat').should_succeed <<EOM
|
371
|
+
cookbook1/
|
372
|
+
cookbook1/metadata.rb
|
373
|
+
cookbook2/
|
374
|
+
cookbook2/metadata.rb
|
375
|
+
cookbook2/recipes/
|
376
|
+
cookbook2/recipes/default.rb
|
377
|
+
EOM
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
when_the_repository 'has a real_cookbooks directory and a cookbooks symlink to it' do
|
383
|
+
directory 'real_cookbooks'
|
384
|
+
symlink 'cookbooks', 'real_cookbooks'
|
385
|
+
|
386
|
+
context 'when cwd is in real_cookbooks/' do
|
387
|
+
cwd 'real_cookbooks'
|
388
|
+
|
389
|
+
it "knife list -Rp --flat returns cookbooks" do
|
390
|
+
knife('list -Rp --flat').should_succeed <<EOM
|
391
|
+
cookbook1/
|
392
|
+
cookbook1/metadata.rb
|
393
|
+
cookbook2/
|
394
|
+
cookbook2/metadata.rb
|
395
|
+
cookbook2/recipes/
|
396
|
+
cookbook2/recipes/default.rb
|
397
|
+
EOM
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
context 'when cwd is in cookbooks/' do
|
402
|
+
cwd 'cookbooks'
|
403
|
+
|
404
|
+
it "knife list -Rp --flat returns cookbooks" do
|
405
|
+
knife('list -Rp --flat').should_succeed <<EOM
|
406
|
+
cookbook1/
|
407
|
+
cookbook1/metadata.rb
|
408
|
+
cookbook2/
|
409
|
+
cookbook2/metadata.rb
|
410
|
+
cookbook2/recipes/
|
411
|
+
cookbook2/recipes/default.rb
|
412
|
+
EOM
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
229
417
|
end
|
230
418
|
|
231
419
|
context "--local" do
|
@@ -310,6 +498,5 @@ EOM
|
|
310
498
|
end
|
311
499
|
end
|
312
500
|
end
|
313
|
-
# TODO different cwd
|
314
501
|
end
|
315
502
|
end
|
@@ -73,10 +73,22 @@ module IntegrationSupport
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
+
def symlink(relative_path, relative_dest)
|
77
|
+
filename = path_to(relative_path)
|
78
|
+
dir = File.dirname(filename)
|
79
|
+
FileUtils.mkdir_p(dir) unless dir == '.'
|
80
|
+
dest_filename = path_to(relative_dest)
|
81
|
+
File.symlink(dest_filename, filename)
|
82
|
+
end
|
83
|
+
|
76
84
|
def path_to(relative_path)
|
77
85
|
File.expand_path(relative_path, (@parent_path || @repository_dir))
|
78
86
|
end
|
79
87
|
|
88
|
+
def self.path_to(relative_path)
|
89
|
+
File.expand_path(relative_path, (@parent_path || @repository_dir))
|
90
|
+
end
|
91
|
+
|
80
92
|
def self.directory(relative_path, &block)
|
81
93
|
before :each do
|
82
94
|
directory(relative_path, &block)
|
@@ -89,6 +101,22 @@ module IntegrationSupport
|
|
89
101
|
end
|
90
102
|
end
|
91
103
|
|
104
|
+
def self.symlink(relative_path, relative_dest)
|
105
|
+
before :each do
|
106
|
+
symlink(relative_path, relative_dest)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.cwd(relative_path)
|
111
|
+
before :each do
|
112
|
+
@old_cwd = Dir.pwd
|
113
|
+
Dir.chdir(path_to(relative_path))
|
114
|
+
end
|
115
|
+
after :each do
|
116
|
+
Dir.chdir(@old_cwd)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
92
120
|
instance_eval(&block)
|
93
121
|
end
|
94
122
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'chef/knife'
|
2
2
|
require 'chef/application/knife'
|
3
|
+
require 'logger'
|
4
|
+
require 'chef/log'
|
3
5
|
|
4
6
|
module KnifeSupport
|
5
7
|
def knife(*args, &block)
|
@@ -16,6 +18,8 @@ module KnifeSupport
|
|
16
18
|
# load stuff ourselves, thank you very much
|
17
19
|
stdout = StringIO.new
|
18
20
|
stderr = StringIO.new
|
21
|
+
old_loggers = Chef::Log.loggers
|
22
|
+
old_log_level = Chef::Log.level
|
19
23
|
begin
|
20
24
|
subcommand_class = Chef::Knife.subcommand_class_from(args)
|
21
25
|
subcommand_class.options = Chef::Application::Knife.options.merge(subcommand_class.options)
|
@@ -27,6 +31,11 @@ module KnifeSupport
|
|
27
31
|
# Don't print stuff
|
28
32
|
Chef::Config[:verbosity] = 0
|
29
33
|
instance.configure_chef
|
34
|
+
logger = Logger.new(stderr)
|
35
|
+
logger.formatter = proc { |severity, datetime, progname, msg| "#{severity}: #{msg}\n" }
|
36
|
+
Chef::Log.use_log_devices([logger])
|
37
|
+
Chef::Log.level = :warn
|
38
|
+
Chef::Log::Formatter.show_time = false
|
30
39
|
instance.run
|
31
40
|
|
32
41
|
exit_code = 0
|
@@ -34,6 +43,9 @@ module KnifeSupport
|
|
34
43
|
# This is how rspec catches exit()
|
35
44
|
rescue SystemExit => e
|
36
45
|
exit_code = e.status
|
46
|
+
ensure
|
47
|
+
Chef::Log.use_log_devices(old_loggers)
|
48
|
+
Chef::Log.level = old_log_level
|
37
49
|
end
|
38
50
|
|
39
51
|
KnifeResult.new(stdout.string, stderr.string, exit_code)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-essentials
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: chef-zero
|