chef 11.12.0.alpha.1 → 11.12.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/lib/chef/api_client/registration.rb +46 -9
  3. data/lib/chef/application.rb +1 -0
  4. data/lib/chef/application/client.rb +25 -24
  5. data/lib/chef/client.rb +34 -0
  6. data/lib/chef/config.rb +11 -0
  7. data/lib/chef/cookbook/chefignore.rb +10 -2
  8. data/lib/chef/cookbook/metadata.rb +31 -3
  9. data/lib/chef/cookbook/synchronizer.rb +2 -2
  10. data/lib/chef/cookbook/syntax_check.rb +4 -4
  11. data/lib/chef/encrypted_data_bag_item.rb +37 -1
  12. data/lib/chef/exceptions.rb +1 -0
  13. data/lib/chef/guard_interpreter/default_guard_interpreter.rb +42 -0
  14. data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +122 -0
  15. data/lib/chef/http.rb +0 -1
  16. data/lib/chef/http/decompressor.rb +7 -4
  17. data/lib/chef/http/simple.rb +5 -0
  18. data/lib/chef/http/validate_content_length.rb +28 -12
  19. data/lib/chef/knife.rb +1 -0
  20. data/lib/chef/knife/client_bulk_delete.rb +48 -9
  21. data/lib/chef/knife/client_delete.rb +4 -4
  22. data/lib/chef/knife/cookbook_bulk_delete.rb +1 -1
  23. data/lib/chef/knife/cookbook_upload.rb +17 -7
  24. data/lib/chef/knife/core/bootstrap_context.rb +1 -1
  25. data/lib/chef/knife/core/ui.rb +42 -5
  26. data/lib/chef/knife/node_run_list_add.rb +31 -2
  27. data/lib/chef/knife/ssh.rb +44 -31
  28. data/lib/chef/knife/ssl_check.rb +213 -0
  29. data/lib/chef/knife/ssl_fetch.rb +145 -0
  30. data/lib/chef/mixin/deep_merge.rb +13 -5
  31. data/lib/chef/mixin/shell_out.rb +9 -3
  32. data/lib/chef/node.rb +23 -4
  33. data/lib/chef/node/immutable_collections.rb +32 -0
  34. data/lib/chef/platform/provider_mapping.rb +21 -18
  35. data/lib/chef/platform/query_helpers.rb +10 -2
  36. data/lib/chef/policy_builder/expand_node_object.rb +3 -6
  37. data/lib/chef/provider/cron.rb +25 -3
  38. data/lib/chef/provider/mount/mount.rb +1 -1
  39. data/lib/chef/provider/package/dpkg.rb +2 -1
  40. data/lib/chef/provider/package/windows.rb +80 -0
  41. data/lib/chef/provider/package/windows/msi.rb +69 -0
  42. data/lib/chef/provider/powershell_script.rb +19 -6
  43. data/lib/chef/provider/service/solaris.rb +11 -7
  44. data/lib/chef/resource.rb +18 -5
  45. data/lib/chef/resource/conditional.rb +20 -7
  46. data/lib/chef/resource/cron.rb +18 -2
  47. data/lib/chef/resource/execute.rb +0 -2
  48. data/lib/chef/resource/powershell_script.rb +23 -1
  49. data/lib/chef/resource/script.rb +25 -0
  50. data/lib/chef/resource/subversion.rb +4 -0
  51. data/lib/chef/resource/windows_package.rb +79 -0
  52. data/lib/chef/resource/windows_script.rb +0 -5
  53. data/lib/chef/resources.rb +1 -0
  54. data/lib/chef/rest.rb +6 -1
  55. data/lib/chef/run_context.rb +22 -2
  56. data/lib/chef/run_context/cookbook_compiler.rb +12 -0
  57. data/lib/chef/util/editor.rb +92 -0
  58. data/lib/chef/util/file_edit.rb +22 -54
  59. data/lib/chef/version.rb +2 -2
  60. data/lib/chef/win32/api/installer.rb +166 -0
  61. data/lib/chef/win32/version.rb +8 -0
  62. data/spec/data/standalone_cookbook/Gemfile +1 -0
  63. data/spec/data/standalone_cookbook/chefignore +9 -0
  64. data/spec/data/standalone_cookbook/recipes/default.rb +3 -0
  65. data/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/multi_json-1.9.0/lib/multi_json.rb +1 -0
  66. data/spec/functional/resource/powershell_spec.rb +262 -1
  67. data/spec/functional/win32/versions_spec.rb +3 -3
  68. data/spec/integration/knife/chefignore_spec.rb +1 -2
  69. data/spec/integration/knife/raw_spec.rb +8 -13
  70. data/spec/integration/knife/redirection_spec.rb +6 -14
  71. data/spec/integration/solo/solo_spec.rb +19 -0
  72. data/spec/support/shared/functional/windows_script.rb +1 -1
  73. data/spec/support/shared/integration/app_server_support.rb +42 -0
  74. data/spec/support/shared/integration/integration_helper.rb +1 -0
  75. data/spec/support/shared/unit/script_resource.rb +38 -0
  76. data/spec/unit/api_client/registration_spec.rb +109 -38
  77. data/spec/unit/application/client_spec.rb +48 -1
  78. data/spec/unit/cookbook/chefignore_spec.rb +10 -0
  79. data/spec/unit/cookbook/metadata_spec.rb +45 -1
  80. data/spec/unit/cookbook/syntax_check_spec.rb +28 -0
  81. data/spec/unit/cookbook_spec.rb +0 -10
  82. data/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb +56 -0
  83. data/spec/unit/http/simple_spec.rb +32 -0
  84. data/spec/unit/http/validate_content_length_spec.rb +187 -0
  85. data/spec/unit/knife/bootstrap_spec.rb +13 -4
  86. data/spec/unit/knife/client_bulk_delete_spec.rb +123 -38
  87. data/spec/unit/knife/client_delete_spec.rb +4 -4
  88. data/spec/unit/knife/cookbook_upload_spec.rb +181 -88
  89. data/spec/unit/knife/core/bootstrap_context_spec.rb +11 -1
  90. data/spec/unit/knife/core/ui_spec.rb +109 -38
  91. data/spec/unit/knife/node_run_list_add_spec.rb +24 -1
  92. data/spec/unit/knife/ssh_spec.rb +17 -6
  93. data/spec/unit/knife/ssl_check_spec.rb +187 -0
  94. data/spec/unit/knife/ssl_fetch_spec.rb +151 -0
  95. data/spec/unit/mixin/deep_merge_spec.rb +17 -0
  96. data/spec/unit/node/immutable_collections_spec.rb +55 -0
  97. data/spec/unit/node_spec.rb +9 -0
  98. data/spec/unit/platform/query_helpers_spec.rb +32 -0
  99. data/spec/unit/platform_spec.rb +193 -175
  100. data/spec/unit/policy_builder/expand_node_object_spec.rb +1 -1
  101. data/spec/unit/provider/cron_spec.rb +175 -1
  102. data/spec/unit/provider/mount/mount_spec.rb +33 -3
  103. data/spec/unit/provider/package/dpkg_spec.rb +4 -0
  104. data/spec/unit/provider/package/windows/msi_spec.rb +60 -0
  105. data/spec/unit/provider/package/windows_spec.rb +80 -0
  106. data/spec/unit/provider/service/macosx_spec.rb +3 -3
  107. data/spec/unit/provider/service/solaris_smf_service_spec.rb +35 -10
  108. data/spec/unit/pure_application_spec.rb +32 -0
  109. data/spec/unit/recipe_spec.rb +4 -0
  110. data/spec/unit/resource/conditional_spec.rb +13 -12
  111. data/spec/unit/resource/cron_spec.rb +7 -2
  112. data/spec/unit/resource/powershell_spec.rb +85 -2
  113. data/spec/unit/resource/subversion_spec.rb +5 -0
  114. data/spec/unit/resource/windows_package_spec.rb +74 -0
  115. data/spec/unit/resource_spec.rb +23 -1
  116. data/spec/unit/rest_spec.rb +15 -0
  117. data/spec/unit/run_context/cookbook_compiler_spec.rb +12 -0
  118. data/spec/unit/run_context_spec.rb +7 -0
  119. data/spec/unit/util/editor_spec.rb +152 -0
  120. data/spec/unit/util/file_edit_spec.rb +37 -1
  121. metadata +41 -30
@@ -124,12 +124,21 @@ describe Chef::Knife::Bootstrap do
124
124
  end
125
125
 
126
126
  describe "specifying no_proxy with various entries" do
127
- subject(:knife) { described_class.new }
128
- let(:options){ ["--bootstrap-no-proxy", setting] }
127
+ subject(:knife) do
128
+ k = described_class.new
129
+ k.instance_variable_set("@template_file", template_file)
130
+ k.parse_options(options)
131
+ k.merge_configs
132
+ k
133
+ end
134
+
135
+ # Include a data bag secret in the options to prevent Bootstrap from
136
+ # attempting to access /etc/chef/encrypted_data_bag_secret, which
137
+ # can fail when the file exists but can't be accessed by the user
138
+ # running the tests.
139
+ let(:options){ ["--bootstrap-no-proxy", setting, "-s", "foo"] }
129
140
  let(:template_file) { File.expand_path(File.join(CHEF_SPEC_DATA, "bootstrap", "no_proxy.erb")) }
130
141
  let(:rendered_template) do
131
- knife.instance_variable_set("@template_file", template_file)
132
- knife.parse_options(options)
133
142
  template_string = knife.read_template
134
143
  knife.render_template(template_string)
135
144
  end
@@ -19,60 +19,145 @@
19
19
  require 'spec_helper'
20
20
 
21
21
  describe Chef::Knife::ClientBulkDelete do
22
- before(:each) do
23
- Chef::Log.logger = Logger.new(StringIO.new)
24
-
25
- Chef::Config[:node_name] = "webmonkey.example.com"
26
- @knife = Chef::Knife::ClientBulkDelete.new
27
- @knife.name_args = ["."]
28
- @stdout = StringIO.new
29
- @knife.ui.stub(:stdout).and_return(@stdout)
30
- @knife.ui.stub(:confirm).and_return(true)
31
- @clients = Hash.new
32
- %w{tim dan stephen}.each do |client_name|
22
+ let(:stdout_io) { StringIO.new }
23
+ let(:stdout) {stdout_io.string}
24
+
25
+ let(:knife) {
26
+ k = Chef::Knife::ClientBulkDelete.new
27
+ k.name_args = name_args
28
+ k.config = option_args
29
+ k.ui.stub(:stdout).and_return(stdout_io)
30
+ k.ui.stub(:confirm).and_return(knife_confirm)
31
+ k.ui.stub(:confirm_without_exit).and_return(knife_confirm)
32
+ k
33
+ }
34
+
35
+ let(:name_args) { [ "." ] }
36
+ let(:option_args) { {} }
37
+
38
+ let(:knife_confirm) { true }
39
+
40
+ let(:nonvalidator_client_names) { %w{tim dan stephen} }
41
+ let(:nonvalidator_clients) {
42
+ clients = Hash.new
43
+
44
+ nonvalidator_client_names.each do |client_name|
33
45
  client = Chef::ApiClient.new()
34
46
  client.name(client_name)
35
47
  client.stub(:destroy).and_return(true)
36
- @clients[client_name] = client
48
+ clients[client_name] = client
49
+ end
50
+
51
+ clients
52
+ }
53
+
54
+ let(:validator_client_names) { %w{myorg-validator} }
55
+ let(:validator_clients) {
56
+ clients = Hash.new
57
+
58
+ validator_client_names.each do |validator_client_name|
59
+ validator_client = Chef::ApiClient.new()
60
+ validator_client.name(validator_client_name)
61
+ validator_client.stub(:validator).and_return(true)
62
+ validator_client.stub(:destroy).and_return(true)
63
+ clients[validator_client_name] = validator_client
37
64
  end
38
- Chef::ApiClient.stub(:list).and_return(@clients)
65
+
66
+ clients
67
+ }
68
+
69
+ let(:client_names) { nonvalidator_client_names + validator_client_names}
70
+ let(:clients) {
71
+ nonvalidator_clients.merge(validator_clients)
72
+ }
73
+
74
+ before(:each) do
75
+ Chef::ApiClient.stub(:list).and_return(clients)
39
76
  end
40
77
 
41
78
  describe "run" do
79
+ describe "without a regex" do
80
+ let(:name_args) { [ ] }
42
81
 
43
- it "should get the list of the clients" do
44
- Chef::ApiClient.should_receive(:list).and_return(@clients)
45
- @knife.run
82
+ it "should exit if the regex is not provided" do
83
+ lambda { knife.run }.should raise_error(SystemExit)
84
+ end
46
85
  end
47
86
 
48
- it "should print the clients you are about to delete" do
49
- @knife.run
50
- @stdout.string.should match(/#{@knife.ui.list(@clients.keys.sort, :columns_down)}/)
51
- end
87
+ describe "with any clients" do
88
+ it "should get the list of the clients" do
89
+ Chef::ApiClient.should_receive(:list)
90
+ knife.run
91
+ end
52
92
 
53
- it "should confirm you really want to delete them" do
54
- @knife.ui.should_receive(:confirm)
55
- @knife.run
56
- end
93
+ it "should print the name of the clients" do
94
+ knife.run
95
+ client_names.each do |client_name|
96
+ stdout.should include(client_name)
97
+ end
98
+ end
57
99
 
58
- it "should delete each client" do
59
- @clients.each_value do |c|
60
- c.should_receive(:destroy)
100
+ it "should confirm you really want to delete them" do
101
+ knife.ui.should_receive(:confirm)
102
+ knife.run
61
103
  end
62
- @knife.run
63
- end
64
104
 
65
- it "should only delete clients that match the regex" do
66
- @knife.name_args = ["tim"]
67
- @clients["tim"].should_receive(:destroy)
68
- @clients["stephen"].should_not_receive(:destroy)
69
- @clients["dan"].should_not_receive(:destroy)
70
- @knife.run
105
+ describe "without --delete-validators" do
106
+ it "should mention that validator clients wont be deleted" do
107
+ knife.run
108
+ stdout.should include("Following clients are validators and will not be deleted.")
109
+ info = stdout.index "Following clients are validators and will not be deleted."
110
+ val = stdout.index "myorg-validator"
111
+ (val > info).should be_true
112
+ end
113
+
114
+ it "should only delete nonvalidator clients" do
115
+ nonvalidator_clients.each_value do |c|
116
+ c.should_receive(:destroy)
117
+ end
118
+
119
+ validator_clients.each_value do |c|
120
+ c.should_not_receive(:destroy)
121
+ end
122
+
123
+ knife.run
124
+ end
125
+ end
126
+
127
+ describe "with --delete-validators" do
128
+ let(:option_args) { {:delete_validators => true} }
129
+
130
+ it "should mention that validator clients will be deleted" do
131
+ knife.run
132
+ stdout.should include("The following validators will be deleted")
133
+ end
134
+
135
+ it "should confirm twice" do
136
+ knife.ui.should_receive(:confirm).once
137
+ knife.ui.should_receive(:confirm_without_exit).once
138
+ knife.run
139
+ end
140
+
141
+ it "should delete all clients" do
142
+ clients.each_value do |c|
143
+ c.should_receive(:destroy)
144
+ end
145
+
146
+ knife.run
147
+ end
148
+ end
71
149
  end
72
150
 
73
- it "should exit if the regex is not provided" do
74
- @knife.name_args = []
75
- lambda { @knife.run }.should raise_error(SystemExit)
151
+ describe "with some clients" do
152
+ let(:name_args) { [ "^ti" ] }
153
+
154
+ it "should only delete clients that match the regex" do
155
+ clients["tim"].should_receive(:destroy)
156
+ clients["stephen"].should_not_receive(:destroy)
157
+ clients["dan"].should_not_receive(:destroy)
158
+ clients["myorg-validator"].should_not_receive(:destroy)
159
+ knife.run
160
+ end
76
161
  end
77
162
  end
78
163
  end
@@ -23,7 +23,7 @@ describe Chef::Knife::ClientDelete do
23
23
  @knife = Chef::Knife::ClientDelete.new
24
24
  # defaults
25
25
  @knife.config = {
26
- :force => false
26
+ :delete_validators => false
27
27
  }
28
28
  @knife.name_args = [ 'adam' ]
29
29
  end
@@ -51,7 +51,7 @@ describe Chef::Knife::ClientDelete do
51
51
  end
52
52
 
53
53
  it 'should delete non-validator client if --force is not set' do
54
- @knife.config[:force] = false
54
+ @knife.config[:delete_validators] = false
55
55
  @client.should_receive(:destroy).and_return(@client)
56
56
  @knife.should_receive(:msg)
57
57
 
@@ -59,7 +59,7 @@ describe Chef::Knife::ClientDelete do
59
59
  end
60
60
 
61
61
  it 'should delete non-validator client if --force is set' do
62
- @knife.config[:force] = true
62
+ @knife.config[:delete_validators] = true
63
63
  @client.should_receive(:destroy).and_return(@client)
64
64
  @knife.should_receive(:msg)
65
65
 
@@ -73,7 +73,7 @@ describe Chef::Knife::ClientDelete do
73
73
  end
74
74
 
75
75
  it 'should delete validator client if --force is set' do
76
- @knife.config[:force] = true
76
+ @knife.config[:delete_validators] = true
77
77
  @client.should_receive(:destroy).and_return(@client)
78
78
  @knife.should_receive(:msg)
79
79
 
@@ -23,178 +23,271 @@ require 'chef/cookbook_uploader'
23
23
  require 'timeout'
24
24
 
25
25
  describe Chef::Knife::CookbookUpload do
26
- before(:each) do
27
- @knife = Chef::Knife::CookbookUpload.new
28
- @knife.name_args = ['test_cookbook']
26
+ let(:cookbook) { Chef::CookbookVersion.new('test_cookbook') }
27
+
28
+ let(:cookbooks_by_name) do
29
+ {cookbook.name => cookbook}
30
+ end
29
31
 
30
- @cookbook = Chef::CookbookVersion.new('test_cookbook')
32
+ let(:cookbook_loader) do
33
+ cookbook_loader = cookbooks_by_name.dup
34
+ cookbook_loader.stub(:merged_cookbooks).and_return([])
35
+ cookbook_loader.stub(:load_cookbooks).and_return(cookbook_loader)
36
+ cookbook_loader
37
+ end
38
+
39
+ let(:cookbook_uploader) { double(:upload_cookbooks => nil) }
31
40
 
32
- @cookbook_loader = {}
33
- @cookbook_loader.stub(:[]).and_return(@cookbook)
34
- @cookbook_loader.stub(:merged_cookbooks).and_return([])
35
- @cookbook_loader.stub(:load_cookbooks).and_return(@cookbook_loader)
36
- Chef::CookbookLoader.stub(:new).and_return(@cookbook_loader)
41
+ let(:output) { StringIO.new }
42
+
43
+ let(:name_args) { ['test_cookbook'] }
37
44
 
38
- @output = StringIO.new
39
- @knife.ui.stub(:stdout).and_return(@output)
40
- @knife.ui.stub(:stderr).and_return(@output)
45
+ let(:knife) do
46
+ k = Chef::Knife::CookbookUpload.new
47
+ k.name_args = name_args
48
+ k.ui.stub(:stdout).and_return(output)
49
+ k.ui.stub(:stderr).and_return(output)
50
+ k
51
+ end
52
+
53
+ before(:each) do
54
+ Chef::CookbookLoader.stub(:new).and_return(cookbook_loader)
41
55
  end
42
56
 
43
57
  describe 'with --concurrency' do
44
58
  it 'should upload cookbooks with predefined concurrency' do
45
- @cookbook_uploader = double(:upload_cookbooks => nil)
46
59
  Chef::CookbookVersion.stub(:list_all_versions).and_return({})
47
- @knife.config[:concurrency] = 3
48
- @test_cookbook = Chef::CookbookVersion.new('test_cookbook')
49
- @cookbook_loader.stub(:each).and_yield("test_cookbook", @test_cookbook)
50
- @cookbook_loader.stub(:cookbook_names).and_return(["test_cookbook"])
60
+ knife.config[:concurrency] = 3
61
+ test_cookbook = Chef::CookbookVersion.new('test_cookbook')
62
+ cookbook_loader.stub(:each).and_yield("test_cookbook", test_cookbook)
63
+ cookbook_loader.stub(:cookbook_names).and_return(["test_cookbook"])
51
64
  Chef::CookbookUploader.should_receive(:new).with( kind_of(Array), kind_of(Array),
52
65
  {:force=>nil, :concurrency => 3}).and_return(double("Chef::CookbookUploader", :upload_cookbooks=> true))
53
- @knife.run
66
+ knife.run
54
67
  end
55
68
  end
56
69
 
57
70
  describe 'run' do
58
71
  before(:each) do
59
- @cookbook_uploader = double(:upload_cookbooks => nil)
60
- Chef::CookbookUploader.stub(:new => @cookbook_uploader)
72
+ Chef::CookbookUploader.stub(:new => cookbook_uploader)
61
73
  Chef::CookbookVersion.stub(:list_all_versions).and_return({})
62
74
  end
63
75
 
64
76
  it 'should print usage and exit when a cookbook name is not provided' do
65
- @knife.name_args = []
66
- @knife.should_receive(:show_usage)
67
- @knife.ui.should_receive(:fatal)
68
- lambda { @knife.run }.should raise_error(SystemExit)
77
+ knife.name_args = []
78
+ knife.should_receive(:show_usage)
79
+ knife.ui.should_receive(:fatal)
80
+ lambda { knife.run }.should raise_error(SystemExit)
69
81
  end
70
82
 
71
83
  describe 'when specifying a cookbook name' do
72
84
  it 'should upload the cookbook' do
73
- @knife.should_receive(:upload).once
74
- @knife.run
85
+ knife.should_receive(:upload).once
86
+ knife.run
75
87
  end
76
88
 
77
89
  it 'should report on success' do
78
- @knife.should_receive(:upload).once
79
- @knife.ui.should_receive(:info).with(/Uploaded 1 cookbook/)
80
- @knife.run
90
+ knife.should_receive(:upload).once
91
+ knife.ui.should_receive(:info).with(/Uploaded 1 cookbook/)
92
+ knife.run
81
93
  end
82
94
  end
83
95
 
84
96
  describe 'when specifying the same cookbook name twice' do
85
97
  it 'should upload the cookbook only once' do
86
- @knife.name_args = ['test_cookbook', 'test_cookbook']
87
- @knife.should_receive(:upload).once
88
- @knife.run
98
+ knife.name_args = ['test_cookbook', 'test_cookbook']
99
+ knife.should_receive(:upload).once
100
+ knife.run
101
+ end
102
+ end
103
+
104
+ context "when uploading a cookbook that uses deprecated overlays" do
105
+
106
+ before do
107
+ cookbook_loader.stub(:merged_cookbooks).and_return(['test_cookbook'])
108
+ cookbook_loader.stub(:merged_cookbook_paths).
109
+ and_return({'test_cookbook' => %w{/path/one/test_cookbook /path/two/test_cookbook}})
110
+ end
111
+
112
+ it "emits a warning" do
113
+ knife.run
114
+ expected_message=<<-E
115
+ WARNING: The cookbooks: test_cookbook exist in multiple places in your cookbook_path.
116
+ A composite version of these cookbooks has been compiled for uploading.
117
+
118
+ IMPORTANT: In a future version of Chef, this behavior will be removed and you will no longer
119
+ be able to have the same version of a cookbook in multiple places in your cookbook_path.
120
+ WARNING: The affected cookbooks are located:
121
+ test_cookbook:
122
+ /path/one/test_cookbook
123
+ /path/two/test_cookbook
124
+ E
125
+ output.string.should include(expected_message)
89
126
  end
90
127
  end
91
128
 
92
129
  describe 'when specifying a cookbook name among many' do
93
- before(:each) do
94
- @knife.name_args = ['test_cookbook1']
95
- @cookbooks = {
130
+ let(:name_args) { ['test_cookbook1'] }
131
+
132
+ let(:cookbooks_by_name) do
133
+ {
96
134
  'test_cookbook1' => Chef::CookbookVersion.new('test_cookbook1'),
97
135
  'test_cookbook2' => Chef::CookbookVersion.new('test_cookbook2'),
98
136
  'test_cookbook3' => Chef::CookbookVersion.new('test_cookbook3')
99
137
  }
100
- @cookbook_loader = {}
101
- @cookbook_loader.stub(:merged_cookbooks).and_return([])
102
- @cookbook_loader.stub(:[]) { |ckbk| @cookbooks[ckbk] }
103
- Chef::CookbookLoader.stub(:new).and_return(@cookbook_loader)
104
138
  end
105
139
 
106
140
  it "should read only one cookbook" do
107
- @cookbook_loader.should_receive(:[]).once.with('test_cookbook1')
108
- @knife.run
141
+ cookbook_loader.should_receive(:[]).once.with('test_cookbook1').and_call_original
142
+ knife.run
109
143
  end
110
144
 
111
145
  it "should not read all cookbooks" do
112
- @cookbook_loader.should_not_receive(:load_cookbooks)
113
- @knife.run
146
+ cookbook_loader.should_not_receive(:load_cookbooks)
147
+ knife.run
114
148
  end
115
149
 
116
150
  it "should upload only one cookbook" do
117
- @knife.should_receive(:upload).exactly(1).times
118
- @knife.run
151
+ knife.should_receive(:upload).exactly(1).times
152
+ knife.run
119
153
  end
120
154
  end
121
155
 
122
156
  # This is testing too much. We should break it up.
123
157
  describe 'when specifying a cookbook name with dependencies' do
158
+ let(:name_args) { ["test_cookbook2"] }
159
+
160
+ let(:cookbooks_by_name) do
161
+ { "test_cookbook1" => test_cookbook1,
162
+ "test_cookbook2" => test_cookbook2,
163
+ "test_cookbook3" => test_cookbook3 }
164
+ end
165
+
166
+ let(:test_cookbook1) { Chef::CookbookVersion.new('test_cookbook1') }
167
+
168
+ let(:test_cookbook2) do
169
+ c = Chef::CookbookVersion.new('test_cookbook2')
170
+ c.metadata.depends("test_cookbook3")
171
+ c
172
+ end
173
+
174
+ let(:test_cookbook3) do
175
+ c = Chef::CookbookVersion.new('test_cookbook3')
176
+ c.metadata.depends("test_cookbook1")
177
+ c.metadata.depends("test_cookbook2")
178
+ c
179
+ end
180
+
124
181
  it "should upload all dependencies once" do
125
- @knife.name_args = ["test_cookbook2"]
126
- @knife.config[:depends] = true
127
- @test_cookbook1 = Chef::CookbookVersion.new('test_cookbook1')
128
- @test_cookbook2 = Chef::CookbookVersion.new('test_cookbook2')
129
- @test_cookbook3 = Chef::CookbookVersion.new('test_cookbook3')
130
- @test_cookbook2.metadata.depends("test_cookbook3")
131
- @test_cookbook3.metadata.depends("test_cookbook1")
132
- @test_cookbook3.metadata.depends("test_cookbook2")
133
- @cookbook_loader.stub(:[]) do |ckbk|
134
- { "test_cookbook1" => @test_cookbook1,
135
- "test_cookbook2" => @test_cookbook2,
136
- "test_cookbook3" => @test_cookbook3 }[ckbk]
137
- end
138
- @knife.stub(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2", "test_cookbook3"])
139
- @knife.should_receive(:upload).exactly(3).times
140
- Timeout::timeout(5) do
141
- @knife.run
182
+ knife.config[:depends] = true
183
+ knife.stub(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2", "test_cookbook3"])
184
+ knife.should_receive(:upload).exactly(3).times
185
+ lambda do
186
+ Timeout::timeout(5) do
187
+ knife.run
188
+ end
142
189
  end.should_not raise_error
143
190
  end
144
191
  end
145
192
 
193
+ describe 'when specifying a cookbook name with missing dependencies' do
194
+ let(:cookbook_dependency) { Chef::CookbookVersion.new('dependency') }
195
+
196
+ before(:each) do
197
+ cookbook.metadata.depends("dependency")
198
+ cookbook_loader.stub(:[]) do |ckbk|
199
+ { "test_cookbook" => cookbook,
200
+ "dependency" => cookbook_dependency}[ckbk]
201
+ end
202
+ knife.stub(:cookbook_names).and_return(["cookbook_dependency", "test_cookbook"])
203
+ @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
204
+ knife.ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
205
+ end
206
+
207
+ it 'should exit and not upload the cookbook' do
208
+ cookbook_loader.should_receive(:[]).once.with('test_cookbook')
209
+ cookbook_loader.should_not_receive(:load_cookbooks)
210
+ cookbook_uploader.should_not_receive(:upload_cookbooks)
211
+ expect {knife.run}.to raise_error(SystemExit)
212
+ end
213
+
214
+ it 'should output a message for a single missing dependency' do
215
+ expect {knife.run}.to raise_error(SystemExit)
216
+ @stderr.string.should include('Cookbook test_cookbook depends on cookbooks which are not currently')
217
+ @stderr.string.should include('being uploaded and cannot be found on the server.')
218
+ @stderr.string.should include("The missing cookbook(s) are: 'dependency' version '>= 0.0.0'")
219
+ end
220
+
221
+ it 'should output a message for a multiple missing dependencies which are concatenated' do
222
+ cookbook_dependency2 = Chef::CookbookVersion.new('dependency2')
223
+ cookbook.metadata.depends("dependency2")
224
+ cookbook_loader.stub(:[]) do |ckbk|
225
+ { "test_cookbook" => cookbook,
226
+ "dependency" => cookbook_dependency,
227
+ "dependency2" => cookbook_dependency2}[ckbk]
228
+ end
229
+ knife.stub(:cookbook_names).and_return(["dependency", "dependency2", "test_cookbook"])
230
+ expect {knife.run}.to raise_error(SystemExit)
231
+ @stderr.string.should include('Cookbook test_cookbook depends on cookbooks which are not currently')
232
+ @stderr.string.should include('being uploaded and cannot be found on the server.')
233
+ @stderr.string.should include("The missing cookbook(s) are:")
234
+ @stderr.string.should include("'dependency' version '>= 0.0.0'")
235
+ @stderr.string.should include("'dependency2' version '>= 0.0.0'")
236
+ end
237
+ end
238
+
146
239
  it "should freeze the version of the cookbooks if --freeze is specified" do
147
- @knife.config[:freeze] = true
148
- @cookbook.should_receive(:freeze_version).once
149
- @knife.run
240
+ knife.config[:freeze] = true
241
+ cookbook.should_receive(:freeze_version).once
242
+ knife.run
150
243
  end
151
244
 
152
245
  describe 'with -a or --all' do
153
246
  before(:each) do
154
- @knife.config[:all] = true
247
+ knife.config[:all] = true
155
248
  @test_cookbook1 = Chef::CookbookVersion.new('test_cookbook1')
156
249
  @test_cookbook2 = Chef::CookbookVersion.new('test_cookbook2')
157
- @cookbook_loader.stub(:each).and_yield("test_cookbook1", @test_cookbook1).and_yield("test_cookbook2", @test_cookbook2)
158
- @cookbook_loader.stub(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2"])
250
+ cookbook_loader.stub(:each).and_yield("test_cookbook1", @test_cookbook1).and_yield("test_cookbook2", @test_cookbook2)
251
+ cookbook_loader.stub(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2"])
159
252
  end
160
253
 
161
254
  it 'should upload all cookbooks' do
162
- @knife.should_receive(:upload).once
163
- @knife.run
255
+ knife.should_receive(:upload).once
256
+ knife.run
164
257
  end
165
258
 
166
259
  it 'should report on success' do
167
- @knife.should_receive(:upload).once
168
- @knife.ui.should_receive(:info).with(/Uploaded all cookbooks/)
169
- @knife.run
260
+ knife.should_receive(:upload).once
261
+ knife.ui.should_receive(:info).with(/Uploaded all cookbooks/)
262
+ knife.run
170
263
  end
171
264
 
172
265
  it 'should update the version constraints for an environment' do
173
- @knife.stub(:assert_environment_valid!).and_return(true)
174
- @knife.config[:environment] = "production"
175
- @knife.should_receive(:update_version_constraints).once
176
- @knife.run
266
+ knife.stub(:assert_environment_valid!).and_return(true)
267
+ knife.config[:environment] = "production"
268
+ knife.should_receive(:update_version_constraints).once
269
+ knife.run
177
270
  end
178
271
  end
179
272
 
180
273
  describe 'when a frozen cookbook exists on the server' do
181
274
  it 'should fail to replace it' do
182
275
  exception = Chef::Exceptions::CookbookFrozen.new
183
- @cookbook_uploader.should_receive(:upload_cookbooks).
276
+ cookbook_uploader.should_receive(:upload_cookbooks).
184
277
  and_raise(exception)
185
- @knife.ui.stub(:error)
186
- @knife.ui.should_receive(:error).with(exception)
187
- lambda { @knife.run }.should raise_error(SystemExit)
278
+ knife.ui.stub(:error)
279
+ knife.ui.should_receive(:error).with(exception)
280
+ lambda { knife.run }.should raise_error(SystemExit)
188
281
  end
189
282
 
190
283
  it 'should not update the version constraints for an environment' do
191
- @knife.stub(:assert_environment_valid!).and_return(true)
192
- @knife.config[:environment] = "production"
193
- @knife.stub(:upload).and_raise(Chef::Exceptions::CookbookFrozen)
194
- @knife.ui.should_receive(:error).with(/Failed to upload 1 cookbook/)
195
- @knife.ui.should_receive(:warn).with(/Not updating version constraints/)
196
- @knife.should_not_receive(:update_version_constraints)
197
- lambda { @knife.run }.should raise_error(SystemExit)
284
+ knife.stub(:assert_environment_valid!).and_return(true)
285
+ knife.config[:environment] = "production"
286
+ knife.stub(:upload).and_raise(Chef::Exceptions::CookbookFrozen)
287
+ knife.ui.should_receive(:error).with(/Failed to upload 1 cookbook/)
288
+ knife.ui.should_receive(:warn).with(/Not updating version constraints/)
289
+ knife.should_not_receive(:update_version_constraints)
290
+ lambda { knife.run }.should raise_error(SystemExit)
198
291
  end
199
292
  end
200
293
  end # run