chef 10.16.6 → 10.18.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- data/distro/common/html/chef-client.8.html +4 -4
- data/distro/common/html/chef-expander.8.html +4 -4
- data/distro/common/html/chef-expanderctl.8.html +4 -4
- data/distro/common/html/chef-server-webui.8.html +4 -4
- data/distro/common/html/chef-server.8.html +4 -4
- data/distro/common/html/chef-solo.8.html +4 -4
- data/distro/common/html/chef-solr.8.html +4 -4
- data/distro/common/html/knife-bootstrap.1.html +4 -4
- data/distro/common/html/knife-client.1.html +4 -4
- data/distro/common/html/knife-configure.1.html +4 -4
- data/distro/common/html/knife-cookbook-site.1.html +4 -4
- data/distro/common/html/knife-cookbook.1.html +4 -4
- data/distro/common/html/knife-data-bag.1.html +4 -4
- data/distro/common/html/knife-environment.1.html +4 -4
- data/distro/common/html/knife-exec.1.html +4 -4
- data/distro/common/html/knife-index.1.html +4 -4
- data/distro/common/html/knife-node.1.html +4 -4
- data/distro/common/html/knife-role.1.html +4 -4
- data/distro/common/html/knife-search.1.html +4 -4
- data/distro/common/html/knife-ssh.1.html +4 -4
- data/distro/common/html/knife-status.1.html +4 -4
- data/distro/common/html/knife-tag.1.html +4 -4
- data/distro/common/html/knife.1.html +4 -4
- data/distro/common/html/shef.1.html +4 -4
- data/distro/common/man/man1/knife-bootstrap.1 +1 -1
- data/distro/common/man/man1/knife-client.1 +1 -1
- data/distro/common/man/man1/knife-configure.1 +1 -1
- data/distro/common/man/man1/knife-cookbook-site.1 +1 -1
- data/distro/common/man/man1/knife-cookbook.1 +1 -1
- data/distro/common/man/man1/knife-data-bag.1 +1 -1
- data/distro/common/man/man1/knife-environment.1 +1 -1
- data/distro/common/man/man1/knife-exec.1 +1 -1
- data/distro/common/man/man1/knife-index.1 +1 -1
- data/distro/common/man/man1/knife-node.1 +1 -1
- data/distro/common/man/man1/knife-role.1 +1 -1
- data/distro/common/man/man1/knife-search.1 +1 -1
- data/distro/common/man/man1/knife-ssh.1 +1 -1
- data/distro/common/man/man1/knife-status.1 +1 -1
- data/distro/common/man/man1/knife-tag.1 +1 -1
- data/distro/common/man/man1/knife.1 +1 -1
- data/distro/common/man/man1/shef.1 +1 -1
- data/distro/common/man/man8/chef-client.8 +1 -1
- data/distro/common/man/man8/chef-expander.8 +1 -1
- data/distro/common/man/man8/chef-expanderctl.8 +1 -1
- data/distro/common/man/man8/chef-server-webui.8 +1 -1
- data/distro/common/man/man8/chef-server.8 +1 -1
- data/distro/common/man/man8/chef-solo.8 +1 -1
- data/distro/common/man/man8/chef-solr.8 +1 -1
- data/lib/chef/api_client.rb +22 -0
- data/lib/chef/cookbook/chefignore.rb +1 -1
- data/lib/chef/cookbook/cookbook_version_loader.rb +1 -3
- data/lib/chef/encrypted_data_bag_item.rb +135 -6
- data/lib/chef/formatters/doc.rb +2 -2
- data/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb +1 -0
- data/lib/chef/knife/bootstrap/archlinux-gems.erb +6 -19
- data/lib/chef/knife/bootstrap/centos5-gems.erb +15 -24
- data/lib/chef/knife/bootstrap/chef-full.erb +9 -22
- data/lib/chef/knife/bootstrap/fedora13-gems.erb +5 -19
- data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +4 -16
- data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +7 -24
- data/lib/chef/knife/bootstrap/ubuntu12.04-gems.erb +5 -19
- data/lib/chef/knife/bootstrap.rb +3 -3
- data/lib/chef/knife/client_reregister.rb +5 -4
- data/lib/chef/knife/cookbook_create.rb +178 -28
- data/lib/chef/knife/cookbook_site_install.rb +1 -1
- data/lib/chef/knife/core/bootstrap_context.rb +2 -2
- data/lib/chef/knife/ssh.rb +31 -15
- data/lib/chef/mixin/recipe_definition_dsl_core.rb +1 -1
- data/lib/chef/platform.rb +8 -0
- data/lib/chef/provider/log.rb +1 -1
- data/lib/chef/provider/package/yum-dump.py +20 -0
- data/lib/chef/provider/package/yum.rb +41 -0
- data/lib/chef/resource/log.rb +10 -1
- data/lib/chef/resource.rb +3 -2
- data/lib/chef/rest.rb +10 -5
- data/lib/chef/version.rb +1 -1
- data/spec/data/cookbooks/chefignore +2 -0
- data/spec/functional/knife/ssh_spec.rb +59 -1
- data/spec/stress/win32/security_spec.rb +4 -4
- data/spec/support/matchers/leak.rb +1 -1
- data/spec/unit/api_client_spec.rb +90 -17
- data/spec/unit/cookbook/chefignore_spec.rb +2 -1
- data/spec/unit/encrypted_data_bag_item_spec.rb +177 -6
- data/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb +20 -0
- data/spec/unit/knife/bootstrap_spec.rb +69 -28
- data/spec/unit/knife/client_reregister_spec.rb +23 -22
- data/spec/unit/knife/cookbook_site_install_spec.rb +12 -2
- data/spec/unit/knife/core/bootstrap_context_spec.rb +7 -0
- data/spec/unit/knife/data_bag_create_spec.rb +5 -0
- data/spec/unit/knife/data_bag_edit_spec.rb +13 -0
- data/spec/unit/knife/data_bag_from_file_spec.rb +15 -1
- data/spec/unit/knife/ssh_spec.rb +93 -1
- data/spec/unit/provider/package/yum_spec.rb +79 -11
- data/spec/unit/resource/log_spec.rb +9 -0
- metadata +854 -850
@@ -23,6 +23,7 @@ describe Chef::Knife::Ssh do
|
|
23
23
|
|
24
24
|
before(:all) do
|
25
25
|
@original_config = Chef::Config.hash_dup
|
26
|
+
@original_knife_config = Chef::Config[:knife].dup
|
26
27
|
Chef::Knife::Ssh.load_deps
|
27
28
|
@server = TinyServer::Manager.new
|
28
29
|
@server.start
|
@@ -30,6 +31,7 @@ describe Chef::Knife::Ssh do
|
|
30
31
|
|
31
32
|
after(:all) do
|
32
33
|
Chef::Config.configuration = @original_config
|
34
|
+
Chef::Config[:knife] = @original_knife_config
|
33
35
|
@server.stop
|
34
36
|
end
|
35
37
|
|
@@ -89,6 +91,19 @@ describe Chef::Knife::Ssh do
|
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
94
|
+
describe "port" do
|
95
|
+
context "when -p 31337 is provided" do
|
96
|
+
before do
|
97
|
+
setup_knife(['-p 31337', '*:*', 'uptime'])
|
98
|
+
end
|
99
|
+
|
100
|
+
it "uses the ssh_port" do
|
101
|
+
@knife.run
|
102
|
+
@knife.config[:ssh_port].should == "31337"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
92
107
|
describe "user" do
|
93
108
|
context "when knife[:ssh_user] is set" do
|
94
109
|
before do
|
@@ -192,9 +207,52 @@ describe Chef::Knife::Ssh do
|
|
192
207
|
end
|
193
208
|
end
|
194
209
|
|
210
|
+
describe "gateway" do
|
211
|
+
context "when knife[:ssh_gateway] is set" do
|
212
|
+
before do
|
213
|
+
setup_knife(['*:*', 'uptime'])
|
214
|
+
Chef::Config[:knife][:ssh_gateway] = "user@ec2.public_hostname"
|
215
|
+
end
|
216
|
+
|
217
|
+
it "uses the ssh_gateway" do
|
218
|
+
@knife.session.should_receive(:via).with("ec2.public_hostname", "user", {})
|
219
|
+
@knife.run
|
220
|
+
@knife.config[:ssh_gateway].should == "user@ec2.public_hostname"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "when -G user@ec2.public_hostname is provided" do
|
225
|
+
before do
|
226
|
+
setup_knife(['-G user@ec2.public_hostname', '*:*', 'uptime'])
|
227
|
+
Chef::Config[:knife][:ssh_gateway] = nil
|
228
|
+
end
|
229
|
+
|
230
|
+
it "uses the ssh_gateway" do
|
231
|
+
@knife.session.should_receive(:via).with("ec2.public_hostname", "user", {})
|
232
|
+
@knife.run
|
233
|
+
@knife.config[:ssh_gateway].should == "user@ec2.public_hostname"
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "when the gateway requires a password" do
|
238
|
+
before do
|
239
|
+
setup_knife(['-G user@ec2.public_hostname', '*:*', 'uptime'])
|
240
|
+
Chef::Config[:knife][:ssh_gateway] = nil
|
241
|
+
@knife.session.stub(:via) do |host, user, options|
|
242
|
+
raise Net::SSH::AuthenticationFailed unless options[:password]
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should prompt the user for a password" do
|
247
|
+
@knife.ui.should_receive(:ask).with("Enter the password for user@ec2.public_hostname: ").and_return("password")
|
248
|
+
@knife.run
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
195
253
|
def setup_knife(params=[])
|
196
254
|
@knife = Chef::Knife::Ssh.new(params)
|
197
|
-
@knife.stub!(:ssh_command).and_return {
|
255
|
+
@knife.stub!(:ssh_command).and_return { 0 }
|
198
256
|
@api = TinyServer::API.instance
|
199
257
|
@api.clear
|
200
258
|
|
@@ -56,11 +56,11 @@ describe 'Chef::ReservedNames::Win32::Security', :windows_only do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should not leak when creating a new ACL and setting it on a file" do
|
59
|
-
securable_object = Security::SecurableObject.new(@monkeyfoo)
|
59
|
+
securable_object = Chef::ReservedNames::Win32::Security::SecurableObject.new(@monkeyfoo)
|
60
60
|
lambda {
|
61
|
-
securable_object.dacl = Security::ACL.create([
|
62
|
-
Chef::ReservedNames::Win32::Security::ACE.access_allowed(Security::SID.Everyone, Chef::ReservedNames::Win32::API::Security::GENERIC_READ),
|
63
|
-
Chef::ReservedNames::Win32::Security::ACE.access_denied(Security::SID.from_account("Users"), Chef::ReservedNames::Win32::API::Security::GENERIC_ALL)
|
61
|
+
securable_object.dacl = Chef::ReservedNames::Win32::Security::ACL.create([
|
62
|
+
Chef::ReservedNames::Win32::Security::ACE.access_allowed(Chef::ReservedNames::Win32::Security::SID.Everyone, Chef::ReservedNames::Win32::API::Security::GENERIC_READ),
|
63
|
+
Chef::ReservedNames::Win32::Security::ACE.access_denied(Chef::ReservedNames::Win32::Security::SID.from_account("Users"), Chef::ReservedNames::Win32::API::Security::GENERIC_ALL)
|
64
64
|
])
|
65
65
|
GC.start
|
66
66
|
}.should_not leak_memory(:warmup => 50, :iterations => 100)
|
@@ -60,7 +60,7 @@ module Matchers
|
|
60
60
|
def profiler
|
61
61
|
@profiler ||= begin
|
62
62
|
if Chef::Platform.windows?
|
63
|
-
require File.join(File.dirname(__FILE__), '..', 'prof', 'win32')
|
63
|
+
require File.join(File.dirname(__FILE__), '..', 'platforms', 'prof', 'win32')
|
64
64
|
RSpec::Prof::Win32::Profiler.new
|
65
65
|
else
|
66
66
|
require File.join(File.dirname(__FILE__), '..', 'prof', 'gc')
|
@@ -151,33 +151,106 @@ describe Chef::ApiClient do
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
|
-
describe "
|
154
|
+
describe "when deserializing from JSON" do
|
155
155
|
before(:each) do
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
156
|
+
client = {
|
157
|
+
"name" => "black",
|
158
|
+
"public_key" => "crowes",
|
159
|
+
"private_key" => "monkeypants",
|
160
|
+
"admin" => true,
|
161
|
+
"json_class" => "Chef::ApiClient"
|
162
|
+
}
|
163
|
+
@client = Chef::JSONCompat.from_json(client.to_json)
|
161
164
|
end
|
162
165
|
|
163
166
|
it "should deserialize to a Chef::ApiClient object" do
|
164
|
-
@
|
167
|
+
@client.should be_a_kind_of(Chef::ApiClient)
|
165
168
|
end
|
166
169
|
|
167
|
-
|
168
|
-
name
|
169
|
-
public_key
|
170
|
-
admin
|
171
|
-
}.each do |t|
|
172
|
-
it "should match '#{t}'" do
|
173
|
-
@deserial.send(t.to_sym).should == @client.send(t.to_sym)
|
174
|
-
end
|
170
|
+
it "preserves the name" do
|
171
|
+
@client.name.should == "black"
|
175
172
|
end
|
176
173
|
|
177
|
-
it "
|
178
|
-
@
|
174
|
+
it "preserves the public key" do
|
175
|
+
@client.public_key.should == "crowes"
|
176
|
+
end
|
177
|
+
|
178
|
+
it "preserves the admin status" do
|
179
|
+
@client.admin.should be_true
|
180
|
+
end
|
181
|
+
|
182
|
+
it "includes the private key if present" do
|
183
|
+
@client.private_key.should == "monkeypants"
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "when requesting a new key" do
|
189
|
+
before do
|
190
|
+
@http_client = mock("Chef::REST mock")
|
191
|
+
Chef::REST.stub!(:new).and_return(@http_client)
|
192
|
+
end
|
193
|
+
|
194
|
+
context "and the client does not exist on the server" do
|
195
|
+
before do
|
196
|
+
@a_404_response = Net::HTTPNotFound.new("404 not found and such", nil, nil)
|
197
|
+
@a_404_exception = Net::HTTPServerException.new("404 not found exception", @a_404_response)
|
198
|
+
|
199
|
+
@http_client.should_receive(:get_rest).with("clients/lost-my-key").and_raise(@a_404_exception)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "raises a 404 error" do
|
203
|
+
lambda { Chef::ApiClient.reregister("lost-my-key") }.should raise_error(Net::HTTPServerException)
|
204
|
+
end
|
179
205
|
end
|
180
206
|
|
207
|
+
context "and the client exists" do
|
208
|
+
before do
|
209
|
+
@api_client_without_key = Chef::ApiClient.new
|
210
|
+
@api_client_without_key.name("lost-my-key")
|
211
|
+
@http_client.should_receive(:get_rest).with("clients/lost-my-key").and_return(@api_client_without_key)
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
context "and the client exists on a Chef 11-like server" do
|
216
|
+
before do
|
217
|
+
@api_client_with_key = Chef::ApiClient.new
|
218
|
+
@api_client_with_key.name("lost-my-key")
|
219
|
+
@api_client_with_key.private_key("the new private key")
|
220
|
+
@http_client.should_receive(:put_rest).
|
221
|
+
with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :private_key => true).
|
222
|
+
and_return(@api_client_with_key)
|
223
|
+
end
|
224
|
+
|
225
|
+
it "returns an ApiClient with a private key" do
|
226
|
+
response = Chef::ApiClient.reregister("lost-my-key")
|
227
|
+
# no sane == method for ApiClient :'(
|
228
|
+
response.should == @api_client_without_key
|
229
|
+
response.private_key.should == "the new private key"
|
230
|
+
response.name.should == "lost-my-key"
|
231
|
+
response.admin.should be_false
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context "and the client exists on a Chef 10-like server" do
|
236
|
+
before do
|
237
|
+
@api_client_with_key = {"name" => "lost-my-key", "private_key" => "the new private key"}
|
238
|
+
@http_client.should_receive(:put_rest).
|
239
|
+
with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :private_key => true).
|
240
|
+
and_return(@api_client_with_key)
|
241
|
+
end
|
242
|
+
|
243
|
+
it "returns an ApiClient with a private key" do
|
244
|
+
response = Chef::ApiClient.reregister("lost-my-key")
|
245
|
+
# no sane == method for ApiClient :'(
|
246
|
+
response.should == @api_client_without_key
|
247
|
+
response.private_key.should == "the new private key"
|
248
|
+
response.name.should == "lost-my-key"
|
249
|
+
response.admin.should be_false
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
181
254
|
end
|
182
255
|
end
|
183
256
|
|
@@ -23,7 +23,7 @@ describe Chef::Cookbook::Chefignore do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it "loads the globs in the chefignore file" do
|
26
|
-
@chefignore.ignores.should =~ %w[recipes/ignoreme.rb]
|
26
|
+
@chefignore.ignores.should =~ %w[recipes/ignoreme.rb ignored]
|
27
27
|
end
|
28
28
|
|
29
29
|
it "removes items from an array that match the ignores" do
|
@@ -32,6 +32,7 @@ describe Chef::Cookbook::Chefignore do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
it "determines if a file is ignored" do
|
35
|
+
@chefignore.ignored?('ignored').should be_true
|
35
36
|
@chefignore.ignored?('recipes/ignoreme.rb').should be_true
|
36
37
|
@chefignore.ignored?('recipes/dontignoreme.rb').should be_false
|
37
38
|
end
|
@@ -19,6 +19,152 @@
|
|
19
19
|
require 'spec_helper'
|
20
20
|
require 'chef/encrypted_data_bag_item'
|
21
21
|
|
22
|
+
module Version0Encryptor
|
23
|
+
def self.encrypt_value(plaintext_data, key)
|
24
|
+
data = plaintext_data.to_yaml
|
25
|
+
|
26
|
+
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
27
|
+
cipher.encrypt
|
28
|
+
cipher.pkcs5_keyivgen(key)
|
29
|
+
encrypted_bytes = cipher.update(data)
|
30
|
+
encrypted_bytes << cipher.final
|
31
|
+
Base64.encode64(encrypted_bytes)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Encryption/serialization code from Chef 11.
|
36
|
+
class Version1Encryptor
|
37
|
+
ALGORITHM = "aes-256-cbc"
|
38
|
+
|
39
|
+
attr_reader :key
|
40
|
+
attr_reader :plaintext_data
|
41
|
+
|
42
|
+
# Create a new Encryptor for +data+, which will be encrypted with the given
|
43
|
+
# +key+.
|
44
|
+
#
|
45
|
+
# === Arguments:
|
46
|
+
# * data: An object of any type that can be serialized to json
|
47
|
+
# * key: A String representing the desired passphrase
|
48
|
+
# * iv: The optional +iv+ parameter is intended for testing use only. When
|
49
|
+
# *not* supplied, Encryptor will use OpenSSL to generate a secure random
|
50
|
+
# IV, which is what you want.
|
51
|
+
def initialize(plaintext_data, key, iv=nil)
|
52
|
+
@plaintext_data = plaintext_data
|
53
|
+
@key = key
|
54
|
+
@iv = iv && Base64.decode64(iv)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a wrapped and encrypted version of +plaintext_data+ suitable for
|
58
|
+
# using as the value in an encrypted data bag item.
|
59
|
+
def for_encrypted_item
|
60
|
+
{
|
61
|
+
"encrypted_data" => encrypted_data,
|
62
|
+
"iv" => Base64.encode64(iv),
|
63
|
+
"version" => 1,
|
64
|
+
"cipher" => ALGORITHM
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
# Generates or returns the IV.
|
69
|
+
def iv
|
70
|
+
# Generated IV comes from OpenSSL::Cipher::Cipher#random_iv
|
71
|
+
# This gets generated when +openssl_encryptor+ gets created.
|
72
|
+
openssl_encryptor if @iv.nil?
|
73
|
+
@iv
|
74
|
+
end
|
75
|
+
|
76
|
+
# Generates (and memoizes) an OpenSSL::Cipher::Cipher object and configures
|
77
|
+
# it for the specified iv and encryption key.
|
78
|
+
def openssl_encryptor
|
79
|
+
@openssl_encryptor ||= begin
|
80
|
+
encryptor = OpenSSL::Cipher::Cipher.new(ALGORITHM)
|
81
|
+
encryptor.encrypt
|
82
|
+
@iv ||= encryptor.random_iv
|
83
|
+
encryptor.iv = @iv
|
84
|
+
encryptor.key = Digest::SHA256.digest(key)
|
85
|
+
encryptor
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Encrypts and Base64 encodes +serialized_data+
|
90
|
+
def encrypted_data
|
91
|
+
@encrypted_data ||= begin
|
92
|
+
enc_data = openssl_encryptor.update(serialized_data)
|
93
|
+
enc_data << openssl_encryptor.final
|
94
|
+
Base64.encode64(enc_data)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Wraps the data in a single key Hash (JSON Object) and converts to JSON.
|
99
|
+
# The wrapper is required because we accept values (such as Integers or
|
100
|
+
# Strings) that do not produce valid JSON when serialized without the
|
101
|
+
# wrapper.
|
102
|
+
def serialized_data
|
103
|
+
Chef::JSONCompat.to_json(:json_wrapper => plaintext_data)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
describe Chef::EncryptedDataBagItem::Decryptor do
|
109
|
+
context "when decrypting a version 1 (JSON+aes-256-cbc+random iv) encrypted value" do
|
110
|
+
before do
|
111
|
+
@encryptor = Version1Encryptor.new({"foo" => "bar"}, "passwd")
|
112
|
+
@encrypted_value = @encryptor.for_encrypted_item
|
113
|
+
|
114
|
+
@decryptor = Chef::EncryptedDataBagItem::Decryptor.for(@encrypted_value, "passwd")
|
115
|
+
end
|
116
|
+
|
117
|
+
it "selects the correct strategy for version 1" do
|
118
|
+
@decryptor.should be_a_kind_of Chef::EncryptedDataBagItem::Decryptor::Version1Decryptor
|
119
|
+
end
|
120
|
+
|
121
|
+
it "decrypts the encrypted value" do
|
122
|
+
@decryptor.decrypted_data.should == {"json_wrapper" => {"foo" => "bar"}}.to_json
|
123
|
+
end
|
124
|
+
|
125
|
+
it "unwraps the encrypted data and returns it" do
|
126
|
+
@decryptor.for_decrypted_item.should == {"foo" => "bar"}
|
127
|
+
end
|
128
|
+
|
129
|
+
context "and the provided key is incorrect" do
|
130
|
+
before do
|
131
|
+
@decryptor = Chef::EncryptedDataBagItem::Decryptor.for(@encrypted_value, "wrong-passwd")
|
132
|
+
end
|
133
|
+
|
134
|
+
it "raises a sensible error" do
|
135
|
+
lambda { @decryptor.for_decrypted_item }.should raise_error(Chef::EncryptedDataBagItem::DecryptionFailure)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "and the cipher is not supported" do
|
140
|
+
before do
|
141
|
+
@encrypted_value["cipher"] = "aes-256-foo"
|
142
|
+
end
|
143
|
+
|
144
|
+
it "raises a sensible error" do
|
145
|
+
lambda { @decryptor.for_decrypted_item }.should raise_error(Chef::EncryptedDataBagItem::UnsupportedCipher)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
context "when decrypting a version 0 (YAML+aes-256-cbc+no iv) encrypted value" do
|
152
|
+
before do
|
153
|
+
@encrypted_value = Version0Encryptor.encrypt_value({"foo" => "bar"}, "passwd")
|
154
|
+
|
155
|
+
@decryptor = Chef::EncryptedDataBagItem::Decryptor.for(@encrypted_value, "passwd")
|
156
|
+
end
|
157
|
+
|
158
|
+
it "selects the correct strategy for version 0" do
|
159
|
+
@decryptor.should be_a_kind_of(Chef::EncryptedDataBagItem::Decryptor::Version0Decryptor)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "decrypts the encrypted value" do
|
163
|
+
@decryptor.for_decrypted_item.should == {"foo" => "bar"}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
22
168
|
describe Chef::EncryptedDataBagItem do
|
23
169
|
before(:each) do
|
24
170
|
@secret = "abc123SECRET"
|
@@ -33,6 +179,10 @@ describe Chef::EncryptedDataBagItem do
|
|
33
179
|
|
34
180
|
describe "encrypting" do
|
35
181
|
|
182
|
+
it "uses version 0 encryption/serialization" do
|
183
|
+
@enc_data["greeting"].should == Version0Encryptor.encrypt_value(@plain_data["greeting"], @secret)
|
184
|
+
end
|
185
|
+
|
36
186
|
it "should not encrypt the 'id' key" do
|
37
187
|
@enc_data["id"].should == "item_name"
|
38
188
|
end
|
@@ -53,12 +203,7 @@ describe Chef::EncryptedDataBagItem do
|
|
53
203
|
end
|
54
204
|
end
|
55
205
|
|
56
|
-
|
57
|
-
before(:each) do
|
58
|
-
@enc_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(@plain_data,
|
59
|
-
@secret)
|
60
|
-
@eh = Chef::EncryptedDataBagItem.new(@enc_data, @secret)
|
61
|
-
end
|
206
|
+
shared_examples_for "a decrypted data bag item" do
|
62
207
|
|
63
208
|
it "doesn't try to decrypt 'id'" do
|
64
209
|
@eh["id"].should == @plain_data["id"]
|
@@ -81,6 +226,32 @@ describe Chef::EncryptedDataBagItem do
|
|
81
226
|
end
|
82
227
|
end
|
83
228
|
|
229
|
+
describe "decrypting" do
|
230
|
+
before(:each) do
|
231
|
+
@enc_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(@plain_data,
|
232
|
+
@secret)
|
233
|
+
@eh = Chef::EncryptedDataBagItem.new(@enc_data, @secret)
|
234
|
+
end
|
235
|
+
|
236
|
+
it_behaves_like "a decrypted data bag item"
|
237
|
+
end
|
238
|
+
|
239
|
+
describe "when decrypting a version 1 (Chef 11.x) data bag item" do
|
240
|
+
before do
|
241
|
+
@enc_data = @plain_data.inject({}) do |encrypted, (key, value)|
|
242
|
+
if key == "id"
|
243
|
+
encrypted["id"] = value
|
244
|
+
else
|
245
|
+
encrypted[key] = Version1Encryptor.new(value, @secret).for_encrypted_item
|
246
|
+
end
|
247
|
+
encrypted
|
248
|
+
end
|
249
|
+
@eh = Chef::EncryptedDataBagItem.new(@enc_data, @secret)
|
250
|
+
end
|
251
|
+
|
252
|
+
it_behaves_like "a decrypted data bag item"
|
253
|
+
end
|
254
|
+
|
84
255
|
describe "loading" do
|
85
256
|
it "should defer to Chef::DataBagItem.load" do
|
86
257
|
Chef::DataBagItem.stub(:load).with(:the_bag, "my_codes").and_return(@enc_data)
|
@@ -112,6 +112,7 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
|
|
112
112
|
# fake code to run through #recipe_snippet
|
113
113
|
source_file = [ "if true", "var = non_existant", "end" ]
|
114
114
|
IO.stub!(:readlines).and_return(source_file)
|
115
|
+
File.stub!(:exists?).and_return(true)
|
115
116
|
end
|
116
117
|
|
117
118
|
it "parses a Windows path" do
|
@@ -127,6 +128,25 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
|
|
127
128
|
@inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception)
|
128
129
|
@inspector.recipe_snippet.should match(/^# In \/home\/btm/)
|
129
130
|
end
|
131
|
+
|
132
|
+
context "when the recipe file does not exist" do
|
133
|
+
before do
|
134
|
+
File.stub!(:exists?).and_return(false)
|
135
|
+
IO.stub!(:readlines).and_raise(Errno::ENOENT)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "does not try to parse a recipe in chef-shell/irb (CHEF-3411)" do
|
139
|
+
@resource.source_line = "(irb#1):1:in `irb_binding'"
|
140
|
+
@inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception)
|
141
|
+
@inspector.recipe_snippet.should be_nil
|
142
|
+
end
|
143
|
+
|
144
|
+
it "does not raise an exception trying to load a non-existant file (CHEF-3411)" do
|
145
|
+
@resource.source_line = "/somewhere/in/space"
|
146
|
+
@inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception)
|
147
|
+
lambda { @inspector.recipe_snippet }.should_not raise_error
|
148
|
+
end
|
149
|
+
end
|
130
150
|
end
|
131
151
|
|
132
152
|
describe "when examining a resource that confuses the parser" do
|
@@ -132,36 +132,77 @@ describe Chef::Knife::Bootstrap do
|
|
132
132
|
@knife.name_args.first.should == "barf"
|
133
133
|
end
|
134
134
|
|
135
|
-
describe "when configuring the underlying knife ssh command"
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
135
|
+
describe "when configuring the underlying knife ssh command"
|
136
|
+
context "from the command line" do
|
137
|
+
before do
|
138
|
+
@knife.name_args = ["foo.example.com"]
|
139
|
+
@knife.config[:ssh_user] = "rooty"
|
140
|
+
@knife.config[:ssh_port] = "4001"
|
141
|
+
@knife.config[:ssh_password] = "open_sesame"
|
142
|
+
Chef::Config[:knife][:ssh_user] = nil
|
143
|
+
Chef::Config[:knife][:ssh_port] = nil
|
144
|
+
@knife.config[:identity_file] = "~/.ssh/me.rsa"
|
145
|
+
@knife.stub!(:read_template).and_return("")
|
146
|
+
@knife_ssh = @knife.knife_ssh
|
147
|
+
end
|
148
|
+
|
149
|
+
it "configures the hostname" do
|
150
|
+
@knife_ssh.name_args.first.should == "foo.example.com"
|
151
|
+
end
|
152
|
+
|
153
|
+
it "configures the ssh user" do
|
154
|
+
@knife_ssh.config[:ssh_user].should == 'rooty'
|
155
|
+
end
|
156
|
+
|
157
|
+
it "configures the ssh password" do
|
158
|
+
@knife_ssh.config[:ssh_password].should == 'open_sesame'
|
159
|
+
end
|
160
|
+
|
161
|
+
it "configures the ssh port" do
|
162
|
+
@knife_ssh.config[:ssh_port].should == '4001'
|
163
|
+
end
|
164
|
+
|
165
|
+
it "configures the ssh identity file" do
|
166
|
+
@knife_ssh.config[:identity_file].should == '~/.ssh/me.rsa'
|
167
|
+
end
|
148
168
|
end
|
149
169
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
170
|
+
context "from the knife config file" do
|
171
|
+
before do
|
172
|
+
@knife.name_args = ["config.example.com"]
|
173
|
+
@knife.config[:ssh_user] = nil
|
174
|
+
@knife.config[:ssh_port] = nil
|
175
|
+
@knife.config[:ssh_gateway] = nil
|
176
|
+
@knife.config[:identity_file] = nil
|
177
|
+
@knife.config[:host_key_verify] = nil
|
178
|
+
Chef::Config[:knife][:ssh_user] = "curiosity"
|
179
|
+
Chef::Config[:knife][:ssh_port] = "2430"
|
180
|
+
Chef::Config[:knife][:identity_file] = "~/.ssh/you.rsa"
|
181
|
+
Chef::Config[:knife][:ssh_gateway] = "towel.blinkenlights.nl"
|
182
|
+
Chef::Config[:knife][:host_key_verify] = true
|
183
|
+
@knife.stub!(:read_template).and_return("")
|
184
|
+
@knife_ssh = @knife.knife_ssh
|
185
|
+
end
|
186
|
+
|
187
|
+
it "configures the ssh user" do
|
188
|
+
@knife_ssh.config[:ssh_user].should == 'curiosity'
|
189
|
+
end
|
190
|
+
|
191
|
+
it "configures the ssh port" do
|
192
|
+
@knife_ssh.config[:ssh_port].should == '2430'
|
193
|
+
end
|
194
|
+
|
195
|
+
it "configures the ssh identity file" do
|
196
|
+
@knife_ssh.config[:identity_file].should == '~/.ssh/you.rsa'
|
197
|
+
end
|
198
|
+
|
199
|
+
it "configures the ssh gateway" do
|
200
|
+
@knife_ssh.config[:ssh_gateway].should == 'towel.blinkenlights.nl'
|
201
|
+
end
|
202
|
+
|
203
|
+
it "configures the host key verify mode" do
|
204
|
+
@knife_ssh.config[:host_key_verify].should == true
|
205
|
+
end
|
165
206
|
end
|
166
207
|
|
167
208
|
describe "when falling back to password auth when host key auth fails" do
|
@@ -22,40 +22,41 @@ describe Chef::Knife::ClientReregister do
|
|
22
22
|
before(:each) do
|
23
23
|
@knife = Chef::Knife::ClientReregister.new
|
24
24
|
@knife.name_args = [ 'adam' ]
|
25
|
-
@client_mock = mock('client_mock')
|
26
|
-
@client_mock.stub!(:save).and_return({ 'private_key' => 'foo_key' })
|
27
|
-
Chef::ApiClient.stub!(:load).and_return(@client_mock)
|
25
|
+
@client_mock = mock('client_mock', :private_key => "foo_key")
|
28
26
|
@stdout = StringIO.new
|
29
27
|
@knife.ui.stub!(:stdout).and_return(@stdout)
|
30
28
|
end
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@client_mock.should_receive(:save).with(true).and_return({'private_key' => 'foo_key'})
|
36
|
-
@knife.run
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'should output the private key' do
|
40
|
-
@knife.run
|
41
|
-
@stdout.string.should match /foo_key/
|
30
|
+
context "when no client name is given on the command line" do
|
31
|
+
before do
|
32
|
+
@knife.name_args = []
|
42
33
|
end
|
43
34
|
|
44
35
|
it 'should print usage and exit when a client name is not provided' do
|
45
|
-
@knife.name_args = []
|
46
36
|
@knife.should_receive(:show_usage)
|
47
37
|
@knife.ui.should_receive(:fatal)
|
48
38
|
lambda { @knife.run }.should raise_error(SystemExit)
|
49
39
|
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when not configured for file output' do
|
43
|
+
it 'reregisters the client and prints the key' do
|
44
|
+
Chef::ApiClient.should_receive(:reregister).with('adam').and_return(@client_mock)
|
45
|
+
@knife.run
|
46
|
+
@stdout.string.should match( /foo_key/ )
|
47
|
+
end
|
48
|
+
end
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
50
|
+
context 'when configured for file output' do
|
51
|
+
it 'should write the private key to a file' do
|
52
|
+
Chef::ApiClient.should_receive(:reregister).with('adam').and_return(@client_mock)
|
53
|
+
|
54
|
+
@knife.config[:file] = '/tmp/monkeypants'
|
55
|
+
filehandle = StringIO.new
|
56
|
+
File.should_receive(:open).with('/tmp/monkeypants', 'w').and_yield(filehandle)
|
57
|
+
@knife.run
|
58
|
+
filehandle.string.should == "foo_key"
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
61
62
|
end
|