chef 11.10.4-x86-mingw32 → 11.12.0.alpha.1-x86-mingw32
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.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +6 -6
- data/README.md +1 -1
- data/lib/chef/api_client.rb +1 -3
- data/lib/chef/application.rb +2 -1
- data/lib/chef/application/client.rb +11 -1
- data/lib/chef/client.rb +24 -9
- data/lib/chef/cookbook/syntax_check.rb +107 -6
- data/lib/chef/dsl/reboot_pending.rb +61 -0
- data/lib/chef/exceptions.rb +12 -1
- data/lib/chef/formatters/error_descriptor.rb +1 -1
- data/lib/chef/http/remote_request_id.rb +46 -0
- data/lib/chef/knife/bootstrap.rb +1 -1
- data/lib/chef/knife/bootstrap/README.md +12 -0
- data/lib/chef/knife/bootstrap/chef-full.erb +3 -0
- data/lib/chef/knife/client_create.rb +6 -0
- data/lib/chef/knife/client_delete.rb +15 -1
- data/lib/chef/knife/raw.rb +1 -0
- data/lib/chef/node.rb +1 -1
- data/lib/chef/node/attribute_collections.rb +8 -1
- data/lib/chef/node/immutable_collections.rb +8 -1
- data/lib/chef/provider/deploy.rb +1 -1
- data/lib/chef/provider/group.rb +1 -1
- data/lib/chef/provider/ifconfig/debian.rb +19 -8
- data/lib/chef/provider/ohai.rb +6 -5
- data/lib/chef/provider/service/macosx.rb +68 -14
- data/lib/chef/recipe.rb +2 -0
- data/lib/chef/request_id.rb +37 -0
- data/lib/chef/resource.rb +2 -0
- data/lib/chef/resource_reporter.rb +7 -4
- data/lib/chef/rest.rb +5 -1
- data/lib/chef/run_status.rb +4 -1
- data/lib/chef/server_api.rb +3 -1
- data/lib/chef/version.rb +2 -2
- data/spec/functional/dsl/reboot_pending_spec.rb +118 -0
- data/spec/functional/resource/base.rb +1 -3
- data/spec/functional/resource/deploy_revision_spec.rb +192 -1
- data/spec/functional/resource/git_spec.rb +1 -1
- data/spec/functional/resource/ohai_spec.rb +65 -0
- data/spec/functional/resource/registry_spec.rb +4 -5
- data/spec/integration/client/client_spec.rb +14 -0
- data/spec/spec_helper.rb +1 -2
- data/spec/support/shared/functional/windows_script.rb +1 -2
- data/spec/unit/api_client_spec.rb +46 -0
- data/spec/unit/client_spec.rb +345 -229
- data/spec/unit/cookbook/syntax_check_spec.rb +0 -1
- data/spec/unit/dsl/reboot_pending_spec.rb +100 -0
- data/spec/unit/knife/client_create_spec.rb +29 -1
- data/spec/unit/knife/client_delete_spec.rb +44 -1
- data/spec/unit/knife_spec.rb +55 -0
- data/spec/unit/node/attribute_spec.rb +7 -0
- data/spec/unit/node/immutable_collections_spec.rb +5 -1
- data/spec/unit/provider/group_spec.rb +5 -0
- data/spec/unit/provider/ifconfig/debian_spec.rb +251 -24
- data/spec/unit/provider/ohai_spec.rb +2 -3
- data/spec/unit/provider/service/macosx_spec.rb +29 -11
- data/spec/unit/resource_reporter_spec.rb +1 -1
- data/spec/unit/rest_spec.rb +38 -13
- metadata +151 -216
@@ -0,0 +1,65 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Serdar Sutay (<serdar@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 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
|
+
|
19
|
+
require 'spec_helper'
|
20
|
+
|
21
|
+
describe Chef::Resource::Ohai do
|
22
|
+
let(:ohai) {
|
23
|
+
o = Ohai::System.new
|
24
|
+
o.all_plugins
|
25
|
+
o
|
26
|
+
}
|
27
|
+
|
28
|
+
let(:node) { Chef::Node.new }
|
29
|
+
|
30
|
+
let(:run_context) {
|
31
|
+
node.default[:platform] = ohai[:platform]
|
32
|
+
node.default[:platform_version] = ohai[:platform_version]
|
33
|
+
events = Chef::EventDispatch::Dispatcher.new
|
34
|
+
Chef::RunContext.new(node, {}, events)
|
35
|
+
}
|
36
|
+
|
37
|
+
shared_examples_for "reloaded :uptime" do
|
38
|
+
it "should reload :uptime" do
|
39
|
+
initial_uptime = ohai[:uptime]
|
40
|
+
|
41
|
+
# Sleep for a second so the uptime gets updated.
|
42
|
+
sleep 1
|
43
|
+
|
44
|
+
ohai_resource.run_action(:reload)
|
45
|
+
node[:uptime].should_not == initial_uptime
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "when reloading all plugins" do
|
50
|
+
let(:ohai_resource) { Chef::Resource::Ohai.new("reload all", run_context)}
|
51
|
+
|
52
|
+
it_behaves_like "reloaded :uptime"
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "when reloading only uptime" do
|
56
|
+
let(:ohai_resource) {
|
57
|
+
r = Chef::Resource::Ohai.new("reload all", run_context)
|
58
|
+
r.plugin("uptime")
|
59
|
+
r
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
it_behaves_like "reloaded :uptime"
|
64
|
+
end
|
65
|
+
end
|
@@ -118,10 +118,10 @@ describe Chef::Resource::RegistryKey, :windows_only do
|
|
118
118
|
|
119
119
|
@resource_reporter = Chef::ResourceReporter.new(@rest_client)
|
120
120
|
@events.register(@resource_reporter)
|
121
|
-
@run_id = @resource_reporter.run_id
|
122
121
|
@run_status = Chef::RunStatus.new(@node, @events)
|
123
|
-
|
124
122
|
@resource_reporter.run_started(@run_status)
|
123
|
+
@run_id = @resource_reporter.run_id
|
124
|
+
|
125
125
|
|
126
126
|
@new_resource.cookbook_name = "monkey"
|
127
127
|
@cookbook_version = double("Cookbook::Version", :version => "1.2.3")
|
@@ -265,7 +265,7 @@ describe Chef::Resource::RegistryKey, :windows_only do
|
|
265
265
|
@new_resource.key(reg_child + '\Slitheen\Raxicoricofallapatorius')
|
266
266
|
@new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
|
267
267
|
@new_resource.recursive(false)
|
268
|
-
|
268
|
+
@new_resource.run_action(:create) # should not raise_error
|
269
269
|
@registry.key_exists?(reg_child + '\Slitheen').should == false
|
270
270
|
@registry.key_exists?(reg_child + '\Slitheen\Raxicoricofallapatorius').should == false
|
271
271
|
end
|
@@ -376,7 +376,7 @@ describe Chef::Resource::RegistryKey, :windows_only do
|
|
376
376
|
@new_resource.key(reg_child + '\Zygons\Zygor')
|
377
377
|
@new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
|
378
378
|
@new_resource.recursive(false)
|
379
|
-
|
379
|
+
@new_resource.run_action(:create_if_missing) # should not raise_error
|
380
380
|
@registry.key_exists?(reg_child + '\Zygons').should == false
|
381
381
|
@registry.key_exists?(reg_child + '\Zygons\Zygor').should == false
|
382
382
|
end
|
@@ -547,7 +547,6 @@ describe Chef::Resource::RegistryKey, :windows_only do
|
|
547
547
|
@new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
|
548
548
|
@new_resource.recursive(false)
|
549
549
|
@new_resource.run_action(:delete_key)
|
550
|
-
@new_resource.should_not raise_error
|
551
550
|
end
|
552
551
|
it "does nothing if the action is delete_key" do
|
553
552
|
@new_resource.key(reg_parent + '\OpscodeWhyRun')
|
@@ -215,5 +215,19 @@ EOM
|
|
215
215
|
result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' -z", :cwd => chef_dir)
|
216
216
|
result.error!
|
217
217
|
end
|
218
|
+
|
219
|
+
it "should complete with success when setting the run list with -r" do
|
220
|
+
file 'config/client.rb', <<EOM
|
221
|
+
chef_server_url 'http://omg.com/blah'
|
222
|
+
cookbook_path "#{path_to('cookbooks')}"
|
223
|
+
EOM
|
224
|
+
|
225
|
+
result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -r 'x::default' -z", :cwd => chef_dir)
|
226
|
+
result.stdout.should_not include("Overridden Run List")
|
227
|
+
result.stdout.should include("Run List is [recipe[x::default]]")
|
228
|
+
#puts result.stdout
|
229
|
+
result.error!
|
230
|
+
end
|
231
|
+
|
218
232
|
end
|
219
233
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -87,8 +87,7 @@ Dir["spec/support/**/*.rb"].
|
|
87
87
|
each { |f| require f }
|
88
88
|
|
89
89
|
OHAI_SYSTEM = Ohai::System.new
|
90
|
-
OHAI_SYSTEM.
|
91
|
-
OHAI_SYSTEM.require_plugin("platform")
|
90
|
+
OHAI_SYSTEM.all_plugins("platform")
|
92
91
|
TEST_PLATFORM = OHAI_SYSTEM["platform"].dup.freeze
|
93
92
|
TEST_PLATFORM_VERSION = OHAI_SYSTEM["platform_version"].dup.freeze
|
94
93
|
|
@@ -23,8 +23,7 @@ shared_context Chef::Resource::WindowsScript do
|
|
23
23
|
before(:all) do
|
24
24
|
|
25
25
|
ohai_reader = Ohai::System.new
|
26
|
-
ohai_reader.
|
27
|
-
ohai_reader.require_plugin("windows::platform")
|
26
|
+
ohai_reader.all_plugins("platform")
|
28
27
|
|
29
28
|
new_node = Chef::Node.new
|
30
29
|
new_node.consume_external_attrs(ohai_reader.data,{})
|
@@ -164,6 +164,52 @@ describe Chef::ApiClient do
|
|
164
164
|
|
165
165
|
end
|
166
166
|
|
167
|
+
describe "when loading from JSON" do
|
168
|
+
before do
|
169
|
+
end
|
170
|
+
|
171
|
+
before(:each) do
|
172
|
+
client = {
|
173
|
+
"name" => "black",
|
174
|
+
"clientname" => "black",
|
175
|
+
"public_key" => "crowes",
|
176
|
+
"private_key" => "monkeypants",
|
177
|
+
"admin" => true,
|
178
|
+
"validator" => true,
|
179
|
+
"json_class" => "Chef::ApiClient"
|
180
|
+
}
|
181
|
+
@http_client = double("Chef::REST mock")
|
182
|
+
Chef::REST.stub(:new).and_return(@http_client)
|
183
|
+
@http_client.should_receive(:get).with("clients/black").and_return(client)
|
184
|
+
@client = Chef::ApiClient.load(client['name'])
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should deserialize to a Chef::ApiClient object" do
|
188
|
+
@client.should be_a_kind_of(Chef::ApiClient)
|
189
|
+
end
|
190
|
+
|
191
|
+
it "preserves the name" do
|
192
|
+
@client.name.should == "black"
|
193
|
+
end
|
194
|
+
|
195
|
+
it "preserves the public key" do
|
196
|
+
@client.public_key.should == "crowes"
|
197
|
+
end
|
198
|
+
|
199
|
+
it "preserves the admin status" do
|
200
|
+
@client.admin.should be_a_kind_of(Chef::TrueClass)
|
201
|
+
end
|
202
|
+
|
203
|
+
it "preserves the 'validator' status" do
|
204
|
+
@client.validator.should be_a_kind_of(Chef::TrueClass)
|
205
|
+
end
|
206
|
+
|
207
|
+
it "includes the private key if present" do
|
208
|
+
@client.private_key.should == "monkeypants"
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
167
213
|
describe "with correctly configured API credentials" do
|
168
214
|
before do
|
169
215
|
Chef::Config[:node_name] = "silent-bob"
|
data/spec/unit/client_spec.rb
CHANGED
@@ -24,29 +24,54 @@ require 'chef/run_context'
|
|
24
24
|
require 'chef/rest'
|
25
25
|
require 'rbconfig'
|
26
26
|
|
27
|
-
|
27
|
+
describe Chef::Client do
|
28
|
+
|
29
|
+
let(:hostname) { "hostname" }
|
30
|
+
let(:machinename) { "machinename.example.org" }
|
31
|
+
let(:fqdn) { "hostname.example.org" }
|
32
|
+
|
33
|
+
let(:ohai_data) do
|
34
|
+
{ :fqdn => fqdn,
|
35
|
+
:hostname => hostname,
|
36
|
+
:machinename => machinename,
|
37
|
+
:platform => 'example-platform',
|
38
|
+
:platform_version => 'example-platform-1.0',
|
39
|
+
:data => {}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:ohai_system) do
|
44
|
+
ohai_system = double( "Ohai::System",
|
45
|
+
:all_plugins => true,
|
46
|
+
:data => ohai_data)
|
47
|
+
ohai_system.stub(:[]) do |key|
|
48
|
+
ohai_data[key]
|
49
|
+
end
|
50
|
+
ohai_system
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:node) do
|
54
|
+
Chef::Node.new.tap do |n|
|
55
|
+
n.name(fqdn)
|
56
|
+
n.chef_environment("_default")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
let(:json_attribs) { nil }
|
61
|
+
let(:client_opts) { {} }
|
62
|
+
|
63
|
+
let(:client) do
|
64
|
+
Chef::Client.new(json_attribs, client_opts).tap do |c|
|
65
|
+
c.node = node
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
28
69
|
before do
|
29
70
|
Chef::Log.logger = Logger.new(StringIO.new)
|
30
71
|
|
31
72
|
# Node/Ohai data
|
32
|
-
|
33
|
-
|
34
|
-
Chef::Config[:node_name] = @fqdn
|
35
|
-
ohai_data = { :fqdn => @fqdn,
|
36
|
-
:hostname => @hostname,
|
37
|
-
:platform => 'example-platform',
|
38
|
-
:platform_version => 'example-platform-1.0',
|
39
|
-
:data => {} }
|
40
|
-
ohai_data.stub(:all_plugins).and_return(true)
|
41
|
-
ohai_data.stub(:data).and_return(ohai_data)
|
42
|
-
Ohai::System.stub(:new).and_return(ohai_data)
|
43
|
-
|
44
|
-
@node = Chef::Node.new
|
45
|
-
@node.name(@fqdn)
|
46
|
-
@node.chef_environment("_default")
|
47
|
-
|
48
|
-
@client = Chef::Client.new
|
49
|
-
@client.node = @node
|
73
|
+
#Chef::Config[:node_name] = fqdn
|
74
|
+
Ohai::System.stub(:new).and_return(ohai_system)
|
50
75
|
end
|
51
76
|
|
52
77
|
describe "authentication protocol selection" do
|
@@ -58,7 +83,7 @@ shared_examples_for Chef::Client do
|
|
58
83
|
it "does not force the authentication protocol to 1.1" do
|
59
84
|
Chef::Config[:node_name] = ("f" * 90)
|
60
85
|
# ugly that this happens as a side effect of a getter :(
|
61
|
-
|
86
|
+
client.node_name
|
62
87
|
Chef::Config[:authentication_protocol_version].should == "1.0"
|
63
88
|
end
|
64
89
|
end
|
@@ -67,7 +92,7 @@ shared_examples_for Chef::Client do
|
|
67
92
|
it "sets the authentication protocol to version 1.1" do
|
68
93
|
Chef::Config[:node_name] = ("f" * 91)
|
69
94
|
# ugly that this happens as a side effect of a getter :(
|
70
|
-
|
95
|
+
client.node_name
|
71
96
|
Chef::Config[:authentication_protocol_version].should == "1.1"
|
72
97
|
end
|
73
98
|
end
|
@@ -75,9 +100,6 @@ shared_examples_for Chef::Client do
|
|
75
100
|
|
76
101
|
describe "configuring output formatters" do
|
77
102
|
context "when no formatter has been configured" do
|
78
|
-
before do
|
79
|
-
@client = Chef::Client.new
|
80
|
-
end
|
81
103
|
|
82
104
|
context "and STDOUT is a TTY" do
|
83
105
|
before do
|
@@ -85,7 +107,7 @@ shared_examples_for Chef::Client do
|
|
85
107
|
end
|
86
108
|
|
87
109
|
it "configures the :doc formatter" do
|
88
|
-
|
110
|
+
client.formatters_for_run.should == [[:doc]]
|
89
111
|
end
|
90
112
|
|
91
113
|
context "and force_logger is set" do
|
@@ -95,7 +117,7 @@ shared_examples_for Chef::Client do
|
|
95
117
|
|
96
118
|
it "configures the :null formatter" do
|
97
119
|
Chef::Config[:force_logger].should be_true
|
98
|
-
|
120
|
+
client.formatters_for_run.should == [[:null]]
|
99
121
|
end
|
100
122
|
|
101
123
|
end
|
@@ -108,7 +130,7 @@ shared_examples_for Chef::Client do
|
|
108
130
|
end
|
109
131
|
|
110
132
|
it "configures the :null formatter" do
|
111
|
-
|
133
|
+
client.formatters_for_run.should == [[:null]]
|
112
134
|
end
|
113
135
|
|
114
136
|
context "and force_formatter is set" do
|
@@ -116,7 +138,7 @@ shared_examples_for Chef::Client do
|
|
116
138
|
Chef::Config[:force_formatter] = true
|
117
139
|
end
|
118
140
|
it "it configures the :doc formatter" do
|
119
|
-
|
141
|
+
client.formatters_for_run.should == [[:doc]]
|
120
142
|
end
|
121
143
|
end
|
122
144
|
end
|
@@ -126,16 +148,15 @@ shared_examples_for Chef::Client do
|
|
126
148
|
context "when a formatter is configured" do
|
127
149
|
context "with no output path" do
|
128
150
|
before do
|
129
|
-
@client = Chef::Client.new
|
130
151
|
Chef::Config.add_formatter(:min)
|
131
152
|
end
|
132
153
|
|
133
154
|
it "does not configure a default formatter" do
|
134
|
-
|
155
|
+
client.formatters_for_run.should == [[:min, nil]]
|
135
156
|
end
|
136
157
|
|
137
158
|
it "configures the formatter for STDOUT/STDERR" do
|
138
|
-
configured_formatters =
|
159
|
+
configured_formatters = client.configure_formatters
|
139
160
|
min_formatter = configured_formatters[0]
|
140
161
|
min_formatter.output.out.should == STDOUT
|
141
162
|
min_formatter.output.err.should == STDERR
|
@@ -144,7 +165,6 @@ shared_examples_for Chef::Client do
|
|
144
165
|
|
145
166
|
context "with an output path" do
|
146
167
|
before do
|
147
|
-
@client = Chef::Client.new
|
148
168
|
@tmpout = Tempfile.open("rspec-for-client-formatter-selection-#{Process.pid}")
|
149
169
|
Chef::Config.add_formatter(:min, @tmpout.path)
|
150
170
|
end
|
@@ -155,7 +175,7 @@ shared_examples_for Chef::Client do
|
|
155
175
|
end
|
156
176
|
|
157
177
|
it "configures the formatter for the file path" do
|
158
|
-
configured_formatters =
|
178
|
+
configured_formatters = client.configure_formatters
|
159
179
|
min_formatter = configured_formatters[0]
|
160
180
|
min_formatter.output.out.path.should == @tmpout.path
|
161
181
|
min_formatter.output.err.path.should == @tmpout.path
|
@@ -165,93 +185,216 @@ shared_examples_for Chef::Client do
|
|
165
185
|
end
|
166
186
|
end
|
167
187
|
|
168
|
-
describe "run" do
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
mock_chef_runner = double("Chef::Runner")
|
175
|
-
|
176
|
-
# --Client.register
|
177
|
-
# Make sure Client#register thinks the client key doesn't
|
178
|
-
# exist, so it tries to register and create one.
|
179
|
-
File.should_receive(:exists?).with(Chef::Config[:client_key]).exactly(1).times.and_return(false)
|
180
|
-
|
181
|
-
# Client.register will register with the validation client name.
|
182
|
-
Chef::ApiClient::Registration.any_instance.should_receive(:run)
|
183
|
-
# Client.register will then turn around create another
|
184
|
-
# Chef::REST object, this time with the client key it got from the
|
185
|
-
# previous step.
|
186
|
-
Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url], @fqdn, Chef::Config[:client_key]).exactly(1).and_return(mock_chef_rest_for_node)
|
187
|
-
|
188
|
-
# --Client#build_node
|
189
|
-
# looks up the node, which we will return, then later saves it.
|
190
|
-
Chef::Node.should_receive(:find_or_create).with(@fqdn).and_return(@node)
|
191
|
-
|
192
|
-
# --ResourceReporter#node_load_completed
|
193
|
-
# gets a run id from the server for storing resource history
|
194
|
-
# (has its own tests, so stubbing it here.)
|
195
|
-
Chef::ResourceReporter.any_instance.should_receive(:node_load_completed)
|
196
|
-
|
197
|
-
# --ResourceReporter#run_completed
|
198
|
-
# updates the server with the resource history
|
199
|
-
# (has its own tests, so stubbing it here.)
|
200
|
-
Chef::ResourceReporter.any_instance.should_receive(:run_completed)
|
201
|
-
# --Client#setup_run_context
|
202
|
-
# ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
|
203
|
-
#
|
204
|
-
Chef::CookbookSynchronizer.any_instance.should_receive(:sync_cookbooks)
|
205
|
-
Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(mock_chef_rest_for_cookbook_sync)
|
206
|
-
mock_chef_rest_for_cookbook_sync.should_receive(:post).with("environments/_default/cookbook_versions", {:run_list => []}).and_return({})
|
207
|
-
|
208
|
-
# --Client#converge
|
209
|
-
Chef::Runner.should_receive(:new).and_return(mock_chef_runner)
|
210
|
-
mock_chef_runner.should_receive(:converge).and_return(true)
|
211
|
-
|
212
|
-
# --Client#save_updated_node
|
213
|
-
Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(mock_chef_rest_for_node_save)
|
214
|
-
mock_chef_rest_for_node_save.should_receive(:put_rest).with("nodes/#{@fqdn}", @node).and_return(true)
|
215
|
-
|
216
|
-
Chef::RunLock.any_instance.should_receive(:acquire)
|
217
|
-
Chef::RunLock.any_instance.should_receive(:save_pid)
|
218
|
-
Chef::RunLock.any_instance.should_receive(:release)
|
219
|
-
|
220
|
-
# Post conditions: check that node has been filled in correctly
|
221
|
-
@client.should_receive(:run_started)
|
222
|
-
@client.should_receive(:run_completed_successfully)
|
188
|
+
describe "a full client run" do
|
189
|
+
shared_examples_for "a successful client run" do
|
190
|
+
let(:http_node_load) { double("Chef::REST (node)") }
|
191
|
+
let(:http_cookbook_sync) { double("Chef::REST (cookbook sync)") }
|
192
|
+
let(:http_node_save) { double("Chef::REST (node save)") }
|
193
|
+
let(:runner) { double("Chef::Runner") }
|
223
194
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
195
|
+
let(:api_client_exists?) { false }
|
196
|
+
|
197
|
+
let(:stdout) { StringIO.new }
|
198
|
+
let(:stderr) { StringIO.new }
|
199
|
+
|
200
|
+
let(:enable_fork) { false }
|
201
|
+
|
202
|
+
def stub_for_register
|
203
|
+
# --Client.register
|
204
|
+
# Make sure Client#register thinks the client key doesn't
|
205
|
+
# exist, so it tries to register and create one.
|
206
|
+
File.should_receive(:exists?).with(Chef::Config[:client_key]).exactly(1).times.and_return(api_client_exists?)
|
207
|
+
|
208
|
+
unless api_client_exists?
|
209
|
+
# Client.register will register with the validation client name.
|
210
|
+
Chef::ApiClient::Registration.any_instance.should_receive(:run)
|
236
211
|
end
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
212
|
+
end
|
213
|
+
|
214
|
+
def stub_for_node_load
|
215
|
+
# Client.register will then turn around create another
|
216
|
+
# Chef::REST object, this time with the client key it got from the
|
217
|
+
# previous step.
|
218
|
+
Chef::REST.should_receive(:new).
|
219
|
+
with(Chef::Config[:chef_server_url], fqdn, Chef::Config[:client_key]).
|
220
|
+
exactly(1).
|
221
|
+
and_return(http_node_load)
|
222
|
+
|
223
|
+
# --Client#build_node
|
224
|
+
# looks up the node, which we will return, then later saves it.
|
225
|
+
Chef::Node.should_receive(:find_or_create).with(fqdn).and_return(node)
|
226
|
+
|
227
|
+
# --ResourceReporter#node_load_completed
|
228
|
+
# gets a run id from the server for storing resource history
|
229
|
+
# (has its own tests, so stubbing it here.)
|
230
|
+
Chef::ResourceReporter.any_instance.should_receive(:node_load_completed)
|
231
|
+
end
|
232
|
+
|
233
|
+
def stub_for_sync_cookbooks
|
234
|
+
# --Client#setup_run_context
|
235
|
+
# ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
|
236
|
+
#
|
237
|
+
Chef::CookbookSynchronizer.any_instance.should_receive(:sync_cookbooks)
|
238
|
+
Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
|
239
|
+
http_cookbook_sync.should_receive(:post).
|
240
|
+
with("environments/_default/cookbook_versions", {:run_list => []}).
|
241
|
+
and_return({})
|
242
|
+
end
|
243
|
+
|
244
|
+
def stub_for_converge
|
245
|
+
# --Client#converge
|
246
|
+
Chef::Runner.should_receive(:new).and_return(runner)
|
247
|
+
runner.should_receive(:converge).and_return(true)
|
248
|
+
|
249
|
+
# --ResourceReporter#run_completed
|
250
|
+
# updates the server with the resource history
|
251
|
+
# (has its own tests, so stubbing it here.)
|
252
|
+
Chef::ResourceReporter.any_instance.should_receive(:run_completed)
|
253
|
+
end
|
254
|
+
|
255
|
+
def stub_for_node_save
|
256
|
+
# --Client#save_updated_node
|
257
|
+
Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_node_save)
|
258
|
+
http_node_save.should_receive(:put_rest).with("nodes/#{fqdn}", node).and_return(true)
|
259
|
+
end
|
260
|
+
|
261
|
+
def stub_for_run
|
262
|
+
Chef::RunLock.any_instance.should_receive(:acquire)
|
263
|
+
Chef::RunLock.any_instance.should_receive(:save_pid)
|
264
|
+
Chef::RunLock.any_instance.should_receive(:release)
|
265
|
+
|
266
|
+
# Post conditions: check that node has been filled in correctly
|
267
|
+
client.should_receive(:run_started)
|
268
|
+
client.should_receive(:run_completed_successfully)
|
269
|
+
end
|
270
|
+
|
271
|
+
before do
|
272
|
+
Chef::Config[:client_fork] = enable_fork
|
273
|
+
|
274
|
+
stub_const("Chef::Client::STDOUT_FD", stdout)
|
275
|
+
stub_const("Chef::Client::STDERR_FD", stderr)
|
276
|
+
|
277
|
+
stub_for_register
|
278
|
+
stub_for_node_load
|
279
|
+
stub_for_sync_cookbooks
|
280
|
+
stub_for_converge
|
281
|
+
stub_for_node_save
|
282
|
+
stub_for_run
|
283
|
+
end
|
284
|
+
|
285
|
+
it "runs ohai, sets up authentication, loads node state, synchronizes policy, and converges" do
|
286
|
+
# This is what we're testing.
|
287
|
+
client.run
|
288
|
+
|
289
|
+
# fork is stubbed, so we can see the outcome of the run
|
290
|
+
node.automatic_attrs[:platform].should == "example-platform"
|
291
|
+
node.automatic_attrs[:platform_version].should == "example-platform-1.0"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
describe "when running chef-client without fork" do
|
297
|
+
|
298
|
+
include_examples "a successful client run"
|
299
|
+
end
|
300
|
+
|
301
|
+
describe "when running chef-client with forking enabled", :unix_only do
|
302
|
+
include_examples "a successful client run" do
|
303
|
+
let(:process_status) do
|
304
|
+
double("Process::Status")
|
305
|
+
end
|
306
|
+
|
307
|
+
let(:enable_fork) { true }
|
308
|
+
|
309
|
+
before do
|
310
|
+
Process.should_receive(:waitpid2).and_return([1, process_status])
|
311
|
+
|
312
|
+
process_status.should_receive(:success?).and_return(true)
|
313
|
+
client.should_receive(:exit).and_return(nil)
|
314
|
+
client.should_receive(:fork).and_yield
|
243
315
|
end
|
244
316
|
end
|
245
317
|
|
246
|
-
|
247
|
-
|
318
|
+
end
|
319
|
+
|
320
|
+
describe "when the client key already exists" do
|
321
|
+
|
322
|
+
let(:api_client_exists?) { true }
|
323
|
+
|
324
|
+
include_examples "a successful client run"
|
325
|
+
end
|
326
|
+
|
327
|
+
describe "when an override run list is given" do
|
328
|
+
let(:client_opts) { {:override_runlist => "recipe[override_recipe]"} }
|
248
329
|
|
249
|
-
|
250
|
-
|
251
|
-
|
330
|
+
it "should permit spaces in overriding run list" do
|
331
|
+
Chef::Client.new(nil, :override_runlist => 'role[a], role[b]')
|
332
|
+
end
|
333
|
+
|
334
|
+
describe "when running the client" do
|
335
|
+
include_examples "a successful client run" do
|
336
|
+
|
337
|
+
before do
|
338
|
+
# Client will try to compile and run override_recipe
|
339
|
+
Chef::RunContext::CookbookCompiler.any_instance.should_receive(:compile)
|
340
|
+
end
|
341
|
+
|
342
|
+
def stub_for_sync_cookbooks
|
343
|
+
# --Client#setup_run_context
|
344
|
+
# ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
|
345
|
+
#
|
346
|
+
Chef::CookbookSynchronizer.any_instance.should_receive(:sync_cookbooks)
|
347
|
+
Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
|
348
|
+
http_cookbook_sync.should_receive(:post).
|
349
|
+
with("environments/_default/cookbook_versions", {:run_list => ["override_recipe"]}).
|
350
|
+
and_return({})
|
351
|
+
end
|
352
|
+
|
353
|
+
def stub_for_node_save
|
354
|
+
# Expect NO node save
|
355
|
+
node.should_not_receive(:save)
|
356
|
+
end
|
357
|
+
end
|
252
358
|
end
|
253
359
|
end
|
254
360
|
|
361
|
+
describe "when a permanent run list is passed as an option" do
|
362
|
+
|
363
|
+
include_examples "a successful client run" do
|
364
|
+
|
365
|
+
let(:new_runlist) { "recipe[new_run_list_recipe]" }
|
366
|
+
let(:client_opts) { {:runlist => new_runlist} }
|
367
|
+
|
368
|
+
def stub_for_sync_cookbooks
|
369
|
+
# --Client#setup_run_context
|
370
|
+
# ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
|
371
|
+
#
|
372
|
+
Chef::CookbookSynchronizer.any_instance.should_receive(:sync_cookbooks)
|
373
|
+
Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
|
374
|
+
http_cookbook_sync.should_receive(:post).
|
375
|
+
with("environments/_default/cookbook_versions", {:run_list => ["new_run_list_recipe"]}).
|
376
|
+
and_return({})
|
377
|
+
end
|
378
|
+
|
379
|
+
before do
|
380
|
+
# Client will try to compile and run the new_run_list_recipe, but we
|
381
|
+
# do not create a fixture for this.
|
382
|
+
Chef::RunContext::CookbookCompiler.any_instance.should_receive(:compile)
|
383
|
+
end
|
384
|
+
|
385
|
+
it "sets the new run list on the node" do
|
386
|
+
client.run
|
387
|
+
node.run_list.should == Chef::RunList.new(new_runlist)
|
388
|
+
end
|
389
|
+
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
end
|
394
|
+
|
395
|
+
|
396
|
+
describe "when handling run failures" do
|
397
|
+
|
255
398
|
it "should remove the run_lock on failure of #load_node" do
|
256
399
|
@run_lock = double("Chef::RunLock", :acquire => true)
|
257
400
|
Chef::RunLock.stub(:new).and_return(@run_lock)
|
@@ -260,64 +403,64 @@ shared_examples_for Chef::Client do
|
|
260
403
|
Chef::EventDispatch::Dispatcher.stub(:new).and_return(@events)
|
261
404
|
|
262
405
|
# @events is created on Chef::Client.new, so we need to recreate it after mocking
|
263
|
-
|
264
|
-
|
406
|
+
client = Chef::Client.new
|
407
|
+
client.stub(:load_node).and_raise(Exception)
|
265
408
|
@run_lock.should_receive(:release)
|
266
409
|
if(Chef::Config[:client_fork] && !windows?)
|
267
|
-
|
410
|
+
client.should_receive(:fork) do |&block|
|
268
411
|
block.call
|
269
412
|
end
|
270
413
|
end
|
271
|
-
lambda {
|
414
|
+
lambda { client.run }.should raise_error(Exception)
|
272
415
|
end
|
416
|
+
end
|
273
417
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
it "notifies observers that the run has started" do
|
284
|
-
notified = false
|
285
|
-
Chef::Client.when_run_starts do |run_status|
|
286
|
-
run_status.node.should == @node
|
287
|
-
notified = true
|
288
|
-
end
|
418
|
+
describe "when notifying other objects of the status of the chef run" do
|
419
|
+
before do
|
420
|
+
Chef::Client.clear_notifications
|
421
|
+
Chef::Node.stub(:find_or_create).and_return(node)
|
422
|
+
node.stub(:save)
|
423
|
+
client.load_node
|
424
|
+
client.build_node
|
425
|
+
end
|
289
426
|
|
290
|
-
|
291
|
-
|
427
|
+
it "notifies observers that the run has started" do
|
428
|
+
notified = false
|
429
|
+
Chef::Client.when_run_starts do |run_status|
|
430
|
+
run_status.node.should == node
|
431
|
+
notified = true
|
292
432
|
end
|
293
433
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
run_status.node.should == @node
|
298
|
-
notified = true
|
299
|
-
end
|
434
|
+
client.run_started
|
435
|
+
notified.should be_true
|
436
|
+
end
|
300
437
|
|
301
|
-
|
302
|
-
|
438
|
+
it "notifies observers that the run has completed successfully" do
|
439
|
+
notified = false
|
440
|
+
Chef::Client.when_run_completes_successfully do |run_status|
|
441
|
+
run_status.node.should == node
|
442
|
+
notified = true
|
303
443
|
end
|
304
444
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
run_status.node.should == @node
|
309
|
-
notified = true
|
310
|
-
end
|
445
|
+
client.run_completed_successfully
|
446
|
+
notified.should be_true
|
447
|
+
end
|
311
448
|
|
312
|
-
|
313
|
-
|
449
|
+
it "notifies observers that the run failed" do
|
450
|
+
notified = false
|
451
|
+
Chef::Client.when_run_fails do |run_status|
|
452
|
+
run_status.node.should == node
|
453
|
+
notified = true
|
314
454
|
end
|
455
|
+
|
456
|
+
client.run_failed
|
457
|
+
notified.should be_true
|
315
458
|
end
|
316
459
|
end
|
317
460
|
|
318
461
|
describe "build_node" do
|
319
462
|
it "should expand the roles and recipes for the node" do
|
320
|
-
|
463
|
+
node.run_list << "role[role_containing_cookbook1]"
|
321
464
|
role_containing_cookbook1 = Chef::Role.new
|
322
465
|
role_containing_cookbook1.name("role_containing_cookbook1")
|
323
466
|
role_containing_cookbook1.run_list << "cookbook1"
|
@@ -329,37 +472,33 @@ shared_examples_for Chef::Client do
|
|
329
472
|
Chef::REST.should_receive(:new).and_return(mock_chef_rest)
|
330
473
|
|
331
474
|
# check pre-conditions.
|
332
|
-
|
333
|
-
|
475
|
+
node[:roles].should be_nil
|
476
|
+
node[:recipes].should be_nil
|
334
477
|
|
335
|
-
|
478
|
+
client.policy_builder.stub(:node).and_return(node)
|
336
479
|
|
337
480
|
# chefspec and possibly others use the return value of this method
|
338
|
-
|
481
|
+
client.build_node.should == node
|
339
482
|
|
340
483
|
# check post-conditions.
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
484
|
+
node[:roles].should_not be_nil
|
485
|
+
node[:roles].length.should == 1
|
486
|
+
node[:roles].should include("role_containing_cookbook1")
|
487
|
+
node[:recipes].should_not be_nil
|
488
|
+
node[:recipes].length.should == 1
|
489
|
+
node[:recipes].should include("cookbook1")
|
347
490
|
end
|
348
491
|
end
|
349
492
|
|
350
493
|
describe "windows_admin_check" do
|
351
|
-
before do
|
352
|
-
@client = Chef::Client.new
|
353
|
-
end
|
354
|
-
|
355
494
|
context "platform is not windows" do
|
356
495
|
before do
|
357
496
|
Chef::Platform.stub(:windows?).and_return(false)
|
358
497
|
end
|
359
498
|
|
360
499
|
it "shouldn't be called" do
|
361
|
-
|
362
|
-
|
500
|
+
client.should_not_receive(:has_admin_privileges?)
|
501
|
+
client.do_windows_admin_check
|
363
502
|
end
|
364
503
|
end
|
365
504
|
|
@@ -369,91 +508,46 @@ shared_examples_for Chef::Client do
|
|
369
508
|
end
|
370
509
|
|
371
510
|
it "should be called" do
|
372
|
-
|
373
|
-
|
511
|
+
client.should_receive(:has_admin_privileges?)
|
512
|
+
client.do_windows_admin_check
|
374
513
|
end
|
375
514
|
|
376
515
|
context "admin privileges exist" do
|
377
516
|
before do
|
378
|
-
|
517
|
+
client.should_receive(:has_admin_privileges?).and_return(true)
|
379
518
|
end
|
380
519
|
|
381
520
|
it "should not log a warning message" do
|
382
521
|
Chef::Log.should_not_receive(:warn)
|
383
|
-
|
522
|
+
client.do_windows_admin_check
|
384
523
|
end
|
385
524
|
|
386
525
|
context "fatal admin check is configured" do
|
387
526
|
it "should not raise an exception" do
|
388
|
-
|
527
|
+
client.do_windows_admin_check #should not raise
|
389
528
|
end
|
390
529
|
end
|
391
530
|
end
|
392
531
|
|
393
532
|
context "admin privileges doesn't exist" do
|
394
533
|
before do
|
395
|
-
|
534
|
+
client.should_receive(:has_admin_privileges?).and_return(false)
|
396
535
|
end
|
397
536
|
|
398
537
|
it "should log a warning message" do
|
399
538
|
Chef::Log.should_receive(:warn)
|
400
|
-
|
539
|
+
client.do_windows_admin_check
|
401
540
|
end
|
402
541
|
|
403
542
|
context "fatal admin check is configured" do
|
404
543
|
it "should raise an exception" do
|
405
|
-
|
544
|
+
client.do_windows_admin_check # should not raise
|
406
545
|
end
|
407
546
|
end
|
408
547
|
end
|
409
548
|
end
|
410
549
|
end
|
411
550
|
|
412
|
-
describe "when a run list override is provided" do
|
413
|
-
before do
|
414
|
-
@node = Chef::Node.new
|
415
|
-
@node.name(@fqdn)
|
416
|
-
@node.chef_environment("_default")
|
417
|
-
@node.automatic_attrs[:platform] = "example-platform"
|
418
|
-
@node.automatic_attrs[:platform_version] = "example-platform-1.0"
|
419
|
-
end
|
420
|
-
|
421
|
-
it "should permit spaces in overriding run list" do
|
422
|
-
@client = Chef::Client.new(nil, :override_runlist => 'role[a], role[b]')
|
423
|
-
end
|
424
|
-
|
425
|
-
it "should override the run list and skip the final node save" do
|
426
|
-
@client = Chef::Client.new(nil, :override_runlist => 'role[test_role]')
|
427
|
-
@client.node = @node
|
428
|
-
|
429
|
-
@node.run_list << "role[role_containing_cookbook1]"
|
430
|
-
|
431
|
-
override_role = Chef::Role.new
|
432
|
-
override_role.name 'test_role'
|
433
|
-
override_role.run_list << 'cookbook1'
|
434
|
-
|
435
|
-
original_runlist = @node.run_list.dup
|
436
|
-
|
437
|
-
mock_chef_rest = double("Chef::REST")
|
438
|
-
mock_chef_rest.should_receive(:get_rest).with("roles/test_role").and_return(override_role)
|
439
|
-
Chef::REST.should_receive(:new).and_return(mock_chef_rest)
|
440
|
-
|
441
|
-
@node.should_not_receive(:save)
|
442
|
-
|
443
|
-
@client.policy_builder.stub(:node).and_return(@node)
|
444
|
-
@client.policy_builder.build_node
|
445
|
-
|
446
|
-
@node[:roles].should_not be_nil
|
447
|
-
@node[:roles].should eql(['test_role'])
|
448
|
-
@node[:recipes].should eql(['cookbook1'])
|
449
|
-
|
450
|
-
@client.save_updated_node
|
451
|
-
|
452
|
-
@node.run_list.should == original_runlist
|
453
|
-
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
551
|
describe "assert_cookbook_path_not_empty" do
|
458
552
|
before do
|
459
553
|
Chef::Config[:solo] = true
|
@@ -462,24 +556,46 @@ shared_examples_for Chef::Client do
|
|
462
556
|
context "when any directory of cookbook_path contains no cookbook" do
|
463
557
|
it "raises CookbookNotFound error" do
|
464
558
|
expect do
|
465
|
-
|
559
|
+
client.send(:assert_cookbook_path_not_empty, nil)
|
466
560
|
end.to raise_error(Chef::Exceptions::CookbookNotFound, 'None of the cookbook paths set in Chef::Config[:cookbook_path], ["/path/to/invalid/cookbook_path"], contain any cookbooks')
|
467
561
|
end
|
468
562
|
end
|
469
563
|
end
|
470
564
|
|
471
|
-
|
565
|
+
describe "setting node name" do
|
566
|
+
context "when machinename, hostname and fqdn are all set" do
|
567
|
+
it "favors the fqdn" do
|
568
|
+
expect(client.node_name).to eql(fqdn)
|
569
|
+
end
|
570
|
+
end
|
472
571
|
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
572
|
+
context "when fqdn is missing" do
|
573
|
+
# ohai 7 should always have machinename == return of hostname
|
574
|
+
let(:fqdn) { nil }
|
575
|
+
it "favors the machinename" do
|
576
|
+
expect(client.node_name).to eql(machinename)
|
577
|
+
end
|
578
|
+
end
|
477
579
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
580
|
+
context "when fqdn and machinename are missing" do
|
581
|
+
# ohai 6 will not have machinename, return the short hostname
|
582
|
+
let(:fqdn) { nil }
|
583
|
+
let(:machinename) { nil }
|
584
|
+
it "falls back to hostname" do
|
585
|
+
expect(client.node_name).to eql(hostname)
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
context "when they're all missing" do
|
590
|
+
let(:machinename) { nil }
|
591
|
+
let(:hostname) { nil }
|
592
|
+
let(:fqdn) { nil }
|
482
593
|
|
483
|
-
|
594
|
+
it "throws an exception" do
|
595
|
+
expect { client.node_name }.to raise_error(Chef::Exceptions::CannotDetermineNodeName)
|
596
|
+
end
|
597
|
+
end
|
484
598
|
|
599
|
+
end
|
485
600
|
end
|
601
|
+
|