clc-cheffish 0.8.clc

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +4 -0
  4. data/Rakefile +23 -0
  5. data/lib/chef/provider/chef_acl.rb +434 -0
  6. data/lib/chef/provider/chef_client.rb +48 -0
  7. data/lib/chef/provider/chef_container.rb +50 -0
  8. data/lib/chef/provider/chef_data_bag.rb +50 -0
  9. data/lib/chef/provider/chef_data_bag_item.rb +273 -0
  10. data/lib/chef/provider/chef_environment.rb +78 -0
  11. data/lib/chef/provider/chef_group.rb +78 -0
  12. data/lib/chef/provider/chef_mirror.rb +138 -0
  13. data/lib/chef/provider/chef_node.rb +82 -0
  14. data/lib/chef/provider/chef_organization.rb +150 -0
  15. data/lib/chef/provider/chef_resolved_cookbooks.rb +41 -0
  16. data/lib/chef/provider/chef_role.rb +79 -0
  17. data/lib/chef/provider/chef_user.rb +53 -0
  18. data/lib/chef/provider/private_key.rb +219 -0
  19. data/lib/chef/provider/public_key.rb +82 -0
  20. data/lib/chef/resource/chef_acl.rb +65 -0
  21. data/lib/chef/resource/chef_client.rb +44 -0
  22. data/lib/chef/resource/chef_container.rb +18 -0
  23. data/lib/chef/resource/chef_data_bag.rb +18 -0
  24. data/lib/chef/resource/chef_data_bag_item.rb +114 -0
  25. data/lib/chef/resource/chef_environment.rb +71 -0
  26. data/lib/chef/resource/chef_group.rb +49 -0
  27. data/lib/chef/resource/chef_mirror.rb +47 -0
  28. data/lib/chef/resource/chef_node.rb +18 -0
  29. data/lib/chef/resource/chef_organization.rb +64 -0
  30. data/lib/chef/resource/chef_resolved_cookbooks.rb +31 -0
  31. data/lib/chef/resource/chef_role.rb +104 -0
  32. data/lib/chef/resource/chef_user.rb +51 -0
  33. data/lib/chef/resource/private_key.rb +44 -0
  34. data/lib/chef/resource/public_key.rb +21 -0
  35. data/lib/cheffish.rb +222 -0
  36. data/lib/cheffish/actor_provider_base.rb +131 -0
  37. data/lib/cheffish/basic_chef_client.rb +115 -0
  38. data/lib/cheffish/chef_provider_base.rb +231 -0
  39. data/lib/cheffish/chef_run_data.rb +19 -0
  40. data/lib/cheffish/chef_run_listener.rb +28 -0
  41. data/lib/cheffish/key_formatter.rb +109 -0
  42. data/lib/cheffish/merged_config.rb +94 -0
  43. data/lib/cheffish/recipe_dsl.rb +147 -0
  44. data/lib/cheffish/server_api.rb +52 -0
  45. data/lib/cheffish/version.rb +3 -0
  46. data/lib/cheffish/with_pattern.rb +21 -0
  47. data/spec/functional/fingerprint_spec.rb +64 -0
  48. data/spec/functional/merged_config_spec.rb +20 -0
  49. data/spec/integration/chef_acl_spec.rb +914 -0
  50. data/spec/integration/chef_client_spec.rb +110 -0
  51. data/spec/integration/chef_container_spec.rb +34 -0
  52. data/spec/integration/chef_group_spec.rb +324 -0
  53. data/spec/integration/chef_mirror_spec.rb +244 -0
  54. data/spec/integration/chef_node_spec.rb +211 -0
  55. data/spec/integration/chef_organization_spec.rb +244 -0
  56. data/spec/integration/chef_user_spec.rb +90 -0
  57. data/spec/integration/private_key_spec.rb +446 -0
  58. data/spec/integration/recipe_dsl_spec.rb +29 -0
  59. data/spec/support/key_support.rb +29 -0
  60. data/spec/support/repository_support.rb +103 -0
  61. data/spec/support/spec_support.rb +176 -0
  62. data/spec/unit/get_private_key_spec.rb +93 -0
  63. metadata +162 -0
@@ -0,0 +1,244 @@
1
+ require 'support/spec_support'
2
+ require 'chef/resource/chef_organization'
3
+ require 'chef/provider/chef_organization'
4
+
5
+ describe Chef::Resource::ChefOrganization do
6
+ extend SpecSupport
7
+
8
+ when_the_chef_server 'is in multi-org mode', :osc_compat => false, :single_org => false do
9
+ context 'and chef_server_url is pointed at the top level' do
10
+ user 'u', {}
11
+ user 'u2', {}
12
+
13
+ it 'chef_organization "x" creates the organization' do
14
+ run_recipe do
15
+ chef_organization 'x'
16
+ end
17
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
18
+ expect(get('/organizations/x')['full_name']).to eq('x')
19
+ end
20
+ end
21
+
22
+ context 'and chef_server_url is pointed at /organizations/foo' do
23
+ organization 'foo'
24
+
25
+ before :each do
26
+ Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, '/organizations/foo').to_s
27
+ end
28
+
29
+ context 'and is empty' do
30
+ user 'u', {}
31
+ user 'u2', {}
32
+
33
+ it 'chef_organization "x" creates the organization' do
34
+ run_recipe do
35
+ chef_organization 'x'
36
+ end
37
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
38
+ expect(get('/organizations/x')['full_name']).to eq('x')
39
+ end
40
+
41
+ it 'chef_organization "x" with full_name creates the organization' do
42
+ run_recipe do
43
+ chef_organization 'x' do
44
+ full_name 'Hi'
45
+ end
46
+ end
47
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
48
+ expect(get('/organizations/x')['full_name']).to eq('Hi')
49
+ end
50
+
51
+ it 'chef_organization "x" and inviting users creates the invites' do
52
+ run_recipe do
53
+ chef_organization 'x' do
54
+ invites 'u', 'u2'
55
+ end
56
+ end
57
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
58
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq(%w(u u2))
59
+ end
60
+
61
+ it 'chef_organization "x" adds members' do
62
+ run_recipe do
63
+ chef_organization 'x' do
64
+ members 'u', 'u2'
65
+ end
66
+ end
67
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
68
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(u u2))
69
+ end
70
+ end
71
+
72
+ context 'and already has an organization named x' do
73
+ user 'u', {}
74
+ user 'u2', {}
75
+ user 'u3', {}
76
+ user 'member', {}
77
+ user 'member2', {}
78
+ user 'invited', {}
79
+ user 'invited2', {}
80
+ organization 'x', { 'full_name' => 'Lo' } do
81
+ org_member 'member', 'member2'
82
+ org_invite 'invited', 'invited2'
83
+ end
84
+
85
+ it 'chef_organization "x" changes nothing' do
86
+ run_recipe do
87
+ chef_organization 'x'
88
+ end
89
+ expect(chef_run).not_to have_updated('chef_organization[x]', :create)
90
+ expect(get('/organizations/x')['full_name']).to eq('Lo')
91
+ end
92
+
93
+ it 'chef_organization "x" with "complete true" reverts the full_name' do
94
+ run_recipe do
95
+ chef_organization 'x' do
96
+ complete true
97
+ end
98
+ end
99
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
100
+ expect(get('/organizations/x')['full_name']).to eq('x')
101
+ end
102
+
103
+ it 'chef_organization "x" with new full_name updates the organization' do
104
+ run_recipe do
105
+ chef_organization 'x' do
106
+ full_name 'Hi'
107
+ end
108
+ end
109
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
110
+ expect(get('/organizations/x')['full_name']).to eq('Hi')
111
+ end
112
+
113
+ context 'invites and membership tests' do
114
+ it 'chef_organization "x" and inviting users creates the invites' do
115
+ run_recipe do
116
+ chef_organization 'x' do
117
+ invites 'u', 'u2'
118
+ end
119
+ end
120
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
121
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq(%w(invited invited2 u u2))
122
+ end
123
+
124
+ it 'chef_organization "x" adds members' do
125
+ run_recipe do
126
+ chef_organization 'x' do
127
+ members 'u', 'u2'
128
+ end
129
+ end
130
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
131
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(member member2 u u2))
132
+ end
133
+
134
+ it 'chef_organization "x" does nothing when inviting already-invited users and members' do
135
+ run_recipe do
136
+ chef_organization 'x' do
137
+ invites 'invited', 'member'
138
+ end
139
+ end
140
+ expect(chef_run).not_to have_updated('chef_organization[x]', :create)
141
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq(%w(invited invited2))
142
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(member member2))
143
+ end
144
+
145
+ it 'chef_organization "x" does nothing when adding members who are already members' do
146
+ run_recipe do
147
+ chef_organization 'x' do
148
+ members 'member'
149
+ end
150
+ end
151
+ expect(chef_run).not_to have_updated('chef_organization[x]', :create)
152
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq(%w(invited invited2))
153
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(member member2))
154
+ end
155
+
156
+ it 'chef_organization "x" upgrades invites to members when asked' do
157
+ run_recipe do
158
+ chef_organization 'x' do
159
+ members 'invited'
160
+ end
161
+ end
162
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
163
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(invited member member2))
164
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq(%w(invited2))
165
+ end
166
+
167
+ it 'chef_organization "x" removes members and invites when asked' do
168
+ run_recipe do
169
+ chef_organization 'x' do
170
+ remove_members 'invited', 'member'
171
+ end
172
+ end
173
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
174
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq(%w(invited2))
175
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(member2))
176
+ end
177
+
178
+ it 'chef_organization "x" does nothing when asked to remove non-members' do
179
+ run_recipe do
180
+ chef_organization 'x' do
181
+ remove_members 'u', 'u2'
182
+ end
183
+ end
184
+ expect(chef_run).not_to have_updated('chef_organization[x]', :create)
185
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq(%w(invited invited2))
186
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(member member2))
187
+ end
188
+
189
+ it 'chef_organization "x" with "complete true" reverts the full_name but does not remove invites or members' do
190
+ run_recipe do
191
+ chef_organization 'x' do
192
+ complete true
193
+ end
194
+ end
195
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
196
+ expect(get('/organizations/x')['full_name']).to eq('x')
197
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq(%w(invited invited2))
198
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(member member2))
199
+ end
200
+
201
+ it 'chef_organization "x" with members [] and "complete true" removes invites and members' do
202
+ run_recipe do
203
+ chef_organization 'x' do
204
+ members []
205
+ complete true
206
+ end
207
+ end
208
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
209
+ expect(get('/organizations/x')['full_name']).to eq('x')
210
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq([])
211
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq([])
212
+ end
213
+
214
+ it 'chef_organization "x" with members [] and "complete true" removes invites but not members' do
215
+ run_recipe do
216
+ chef_organization 'x' do
217
+ invites []
218
+ complete true
219
+ end
220
+ end
221
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
222
+ expect(get('/organizations/x')['full_name']).to eq('x')
223
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq([])
224
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(member member2))
225
+ end
226
+
227
+ it 'chef_organization "x" with invites, members and "complete true" removes all non-specified invites and members' do
228
+ run_recipe do
229
+ chef_organization 'x' do
230
+ invites 'invited', 'u'
231
+ members 'member', 'u2'
232
+ complete true
233
+ end
234
+ end
235
+ expect(chef_run).to have_updated('chef_organization[x]', :create)
236
+ expect(get('/organizations/x')['full_name']).to eq('x')
237
+ expect(get('/organizations/x/association_requests').map { |u| u['username'] }).to eq(%w(invited u))
238
+ expect(get('/organizations/x/users').map { |u| u['user']['username'] }).to eq(%w(member u2))
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,90 @@
1
+ require 'support/spec_support'
2
+ require 'support/key_support'
3
+ require 'chef/resource/chef_user'
4
+ require 'chef/provider/chef_user'
5
+
6
+ repo_path = Dir.mktmpdir('chef_repo')
7
+
8
+ describe Chef::Resource::ChefUser do
9
+ extend SpecSupport
10
+
11
+ with_recipe do
12
+ private_key "#{repo_path}/blah.pem"
13
+ end
14
+
15
+ when_the_chef_server 'is empty' do
16
+ context 'and we run a recipe that creates user "blah"'do
17
+ with_converge do
18
+ chef_user 'blah' do
19
+ source_key_path "#{repo_path}/blah.pem"
20
+ end
21
+ end
22
+
23
+ it 'the user gets created' do
24
+ expect(chef_run).to have_updated 'chef_user[blah]', :create
25
+ user = get('/users/blah')
26
+ expect(user['name']).to eq('blah')
27
+ key, format = Cheffish::KeyFormatter.decode(user['public_key'])
28
+ expect(key).to be_public_key_for("#{repo_path}/blah.pem")
29
+ end
30
+ end
31
+
32
+ context 'and we run a recipe that creates user "blah" with output_key_path' do
33
+ with_converge do
34
+ chef_user 'blah' do
35
+ source_key_path "#{repo_path}/blah.pem"
36
+ output_key_path "#{repo_path}/blah.pub"
37
+ end
38
+ end
39
+
40
+ it 'the output public key gets created' do
41
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
42
+ expect("#{repo_path}/blah.pub").to be_public_key_for("#{repo_path}/blah.pem")
43
+ end
44
+ end
45
+ end
46
+
47
+ when_the_chef_server 'is multitenant', :osc_compat => false, :single_org => false do
48
+ context 'and chef_server_url is pointed at the top level' do
49
+ context 'and we run a recipe that creates user "blah"'do
50
+ with_converge do
51
+ chef_user 'blah' do
52
+ source_key_path "#{repo_path}/blah.pem"
53
+ end
54
+ end
55
+
56
+ it 'the user gets created' do
57
+ expect(chef_run).to have_updated 'chef_user[blah]', :create
58
+ user = get('/users/blah')
59
+ expect(user['name']).to eq('blah')
60
+ key, format = Cheffish::KeyFormatter.decode(user['public_key'])
61
+ expect(key).to be_public_key_for("#{repo_path}/blah.pem")
62
+ end
63
+ end
64
+ end
65
+
66
+ context 'and chef_server_url is pointed at /organizations/foo' do
67
+ organization 'foo'
68
+
69
+ before :each do
70
+ Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, '/organizations/foo').to_s
71
+ end
72
+
73
+ context 'and we run a recipe that creates user "blah"'do
74
+ with_converge do
75
+ chef_user 'blah' do
76
+ source_key_path "#{repo_path}/blah.pem"
77
+ end
78
+ end
79
+
80
+ it 'the user gets created' do
81
+ expect(chef_run).to have_updated 'chef_user[blah]', :create
82
+ user = get('/users/blah')
83
+ expect(user['name']).to eq('blah')
84
+ key, format = Cheffish::KeyFormatter.decode(user['public_key'])
85
+ expect(key).to be_public_key_for("#{repo_path}/blah.pem")
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,446 @@
1
+ require 'support/spec_support'
2
+ require 'chef/resource/private_key'
3
+ require 'chef/provider/private_key'
4
+ require 'chef/resource/public_key'
5
+ require 'chef/provider/public_key'
6
+ require 'support/key_support'
7
+
8
+ repo_path = Dir.mktmpdir('chef_repo')
9
+
10
+ describe Chef::Resource::PrivateKey do
11
+ extend SpecSupport
12
+
13
+ before :each do
14
+ FileUtils.remove_entry_secure(repo_path)
15
+ Dir.mkdir(repo_path)
16
+ end
17
+
18
+ context 'with a recipe with a private_key' do
19
+ with_recipe do
20
+ private_key "#{repo_path}/blah"
21
+ end
22
+
23
+ it 'the private_key is created in pem format' do
24
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah]", :create
25
+ expect(IO.read("#{repo_path}/blah")).to start_with('-----BEGIN')
26
+ expect(OpenSSL::PKey.read(IO.read("#{repo_path}/blah"))).to be_kind_of(OpenSSL::PKey::RSA)
27
+ end
28
+ end
29
+
30
+ context 'with a private_key "blah" resource' do
31
+ before :each do
32
+ Dir.mkdir("#{repo_path}/other_keys")
33
+ Chef::Config.private_key_paths = [ repo_path, "#{repo_path}/other_keys" ]
34
+ end
35
+
36
+ with_recipe do
37
+ private_key 'blah'
38
+ end
39
+
40
+ it 'the private key is created in the private_key_write_path' do
41
+ expect(chef_run).to have_updated "private_key[blah]", :create
42
+ expect(Chef::Config.private_key_write_path).to eq(repo_path)
43
+ expect(File.exist?("#{repo_path}/blah")).to be true
44
+ expect(File.exist?("#{repo_path}/other_keys/blah")).to be false
45
+ expect(OpenSSL::PKey.read(IO.read("#{repo_path}/blah"))).to be_kind_of(OpenSSL::PKey::RSA)
46
+ expect(OpenSSL::PKey.read(Cheffish.get_private_key('blah'))).to be_kind_of(OpenSSL::PKey::RSA)
47
+ end
48
+
49
+ context 'and the private key already exists somewhere not in the write path' do
50
+ before :each do
51
+ Cheffish::BasicChefClient.converge_block do
52
+ private_key "#{repo_path}/other_keys/blah"
53
+ end
54
+ end
55
+
56
+ it 'the private expect(key).to not update' do
57
+ expect(chef_run).not_to have_updated "private_key[blah]", :create
58
+
59
+ expect(File.exist?("#{repo_path}/blah")).to be false
60
+ expect(File.exist?("#{repo_path}/other_keys/blah")).to be true
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'with a private key' do
66
+ before :each do
67
+ Cheffish::BasicChefClient.converge_block do
68
+ private_key "#{repo_path}/blah"
69
+ end
70
+ end
71
+
72
+ context 'and a private_key that copies it in der format' do
73
+ with_converge do
74
+ private_key "#{repo_path}/blah.der" do
75
+ source_key_path "#{repo_path}/blah"
76
+ format :der
77
+ end
78
+ end
79
+
80
+ it 'the private_key is copied in der format and is identical' do
81
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah.der]", :create
82
+ key_str = IO.read("#{repo_path}/blah.der")
83
+ expect(key_str).not_to start_with('-----BEGIN')
84
+ expect(key_str).not_to start_with('ssh-')
85
+ expect("#{repo_path}/blah.der").to match_private_key("#{repo_path}/blah")
86
+ end
87
+ end
88
+
89
+ it 'a private_key that copies it from in-memory as a string succeeds' do
90
+ run_recipe do
91
+ private_key "#{repo_path}/blah.der" do
92
+ source_key IO.read("#{repo_path}/blah")
93
+ format :der
94
+ end
95
+ end
96
+
97
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah.der]", :create
98
+ key_str = IO.read("#{repo_path}/blah.der")
99
+ expect(key_str).not_to start_with('-----BEGIN')
100
+ expect(key_str).not_to start_with('ssh-')
101
+ expect("#{repo_path}/blah.der").to match_private_key("#{repo_path}/blah")
102
+ end
103
+
104
+ it 'a private_key that copies it from in-memory as a key succeeds' do
105
+ key = OpenSSL::PKey.read(IO.read("#{repo_path}/blah"))
106
+ run_recipe do
107
+ private_key "#{repo_path}/blah.der" do
108
+ source_key key
109
+ format :der
110
+ end
111
+ end
112
+
113
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah.der]", :create
114
+ key_str = IO.read("#{repo_path}/blah.der")
115
+ expect(key_str).not_to start_with('-----BEGIN')
116
+ expect(key_str).not_to start_with('ssh-')
117
+ expect("#{repo_path}/blah.der").to match_private_key("#{repo_path}/blah")
118
+ end
119
+
120
+ context 'and a public_key recipe' do
121
+ with_converge do
122
+ public_key "#{repo_path}/blah.pub" do
123
+ source_key_path "#{repo_path}/blah"
124
+ end
125
+ end
126
+
127
+ it 'the public_key is created' do
128
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub]", :create
129
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
130
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah"
131
+ end
132
+ end
133
+
134
+ context 'and a public key' do
135
+ before :each do
136
+ Cheffish::BasicChefClient.converge_block do
137
+ public_key "#{repo_path}/blah.pub" do
138
+ source_key_path "#{repo_path}/blah"
139
+ end
140
+ end
141
+ end
142
+
143
+ context 'and public_key resource based off the public key file' do
144
+ with_converge do
145
+ public_key "#{repo_path}/blah.pub2" do
146
+ source_key_path "#{repo_path}/blah.pub"
147
+ end
148
+ end
149
+
150
+ it 'the second public_key is created' do
151
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub2]", :create
152
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
153
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah"
154
+ end
155
+ end
156
+
157
+ context 'and another public_key based off the first public_key in-memory in a string' do
158
+ with_converge do
159
+ public_key "#{repo_path}/blah.pub2" do
160
+ source_key IO.read("#{repo_path}/blah.pub")
161
+ end
162
+ end
163
+
164
+ it 'the second public_key is created' do
165
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub2]", :create
166
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
167
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah"
168
+ end
169
+ end
170
+
171
+ it 'and another public_key based off the first public_key in-memory in a key, the second public_key is created' do
172
+ key, format = Cheffish::KeyFormatter.decode(IO.read("#{repo_path}/blah.pub"))
173
+
174
+ run_recipe do
175
+ public_key "#{repo_path}/blah.pub2" do
176
+ source_key key
177
+ end
178
+ end
179
+
180
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub2]", :create
181
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
182
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah"
183
+ end
184
+
185
+ context 'and another public_key in :pem format based off the first public_key' do
186
+ with_converge do
187
+ public_key "#{repo_path}/blah.pub2" do
188
+ source_key_path "#{repo_path}/blah.pub"
189
+ format :pem
190
+ end
191
+ end
192
+
193
+ it 'the second public_key is created' do
194
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub2]", :create
195
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
196
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah"
197
+ end
198
+ end
199
+
200
+ context 'and another public_key in :der format based off the first public_key' do
201
+ with_converge do
202
+ public_key "#{repo_path}/blah.pub2" do
203
+ source_key_path "#{repo_path}/blah.pub"
204
+ format :pem
205
+ end
206
+ end
207
+
208
+ it 'the second public_key is created' do
209
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub2]", :create
210
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
211
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah"
212
+ end
213
+ end
214
+ end
215
+
216
+ context 'and a public_key resource in pem format' do
217
+ with_converge do
218
+ public_key "#{repo_path}/blah.pub" do
219
+ source_key_path "#{repo_path}/blah"
220
+ format :pem
221
+ end
222
+ end
223
+
224
+ it 'the public_key is created' do
225
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub]", :create
226
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('-----BEGIN')
227
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah"
228
+ end
229
+ end
230
+
231
+ context 'and a public_key resource in der format' do
232
+ with_converge do
233
+ public_key "#{repo_path}/blah.pub" do
234
+ source_key_path "#{repo_path}/blah"
235
+ format :der
236
+ end
237
+ end
238
+
239
+ it 'the public_key is created in openssh format' do
240
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub]", :create
241
+ expect(IO.read("#{repo_path}/blah.pub")).not_to start_with('-----BEGIN')
242
+ expect(IO.read("#{repo_path}/blah.pub")).not_to start_with('ssh-rsa')
243
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah"
244
+ end
245
+ end
246
+ end
247
+
248
+ context 'with a recipe with a private_key in der format' do
249
+ with_recipe do
250
+ private_key "#{repo_path}/blah" do
251
+ format :der
252
+ end
253
+ end
254
+
255
+ it 'the private_key is created' do
256
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah]", :create
257
+ expect(IO.read("#{repo_path}/blah")).not_to start_with('-----BEGIN')
258
+ expect(OpenSSL::PKey.read(IO.read("#{repo_path}/blah"))).to be_kind_of(OpenSSL::PKey::RSA)
259
+ end
260
+ end
261
+
262
+ context 'with a private key in der format' do
263
+ before :each do
264
+ Cheffish::BasicChefClient.converge_block do
265
+ private_key "#{repo_path}/blah" do
266
+ format :der
267
+ end
268
+ end
269
+ end
270
+
271
+ context 'and a public_key' do
272
+ with_converge do
273
+ public_key "#{repo_path}/blah.pub" do
274
+ source_key_path "#{repo_path}/blah"
275
+ end
276
+ end
277
+
278
+ it 'the public_key is created in openssh format' do
279
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub]", :create
280
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
281
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah"
282
+ end
283
+ end
284
+ end
285
+
286
+ context 'with a recipe with a private_key with a pass_phrase' do
287
+ with_converge do
288
+ private_key "#{repo_path}/blah" do
289
+ pass_phrase 'hello'
290
+ end
291
+ end
292
+
293
+ it 'the private_key is created' do
294
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah]", :create
295
+ expect(IO.read("#{repo_path}/blah")).to start_with('-----BEGIN')
296
+ expect(OpenSSL::PKey.read(IO.read("#{repo_path}/blah"), 'hello')).to be_kind_of(OpenSSL::PKey::RSA)
297
+ end
298
+ end
299
+
300
+ context 'with a private key with a pass phrase' do
301
+ before :each do
302
+ Cheffish::BasicChefClient.converge_block do
303
+ private_key "#{repo_path}/blah" do
304
+ pass_phrase 'hello'
305
+ end
306
+ end
307
+ end
308
+
309
+ context 'and a private_key that copies it in der format' do
310
+ with_converge do
311
+ private_key "#{repo_path}/blah.der" do
312
+ source_key_path "#{repo_path}/blah"
313
+ source_key_pass_phrase 'hello'
314
+ format :der
315
+ end
316
+ end
317
+
318
+ it 'the private_key is copied in der format and is identical' do
319
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah.der]", :create
320
+ key_str = IO.read("#{repo_path}/blah.der")
321
+ expect(key_str).not_to start_with('-----BEGIN')
322
+ expect(key_str).not_to start_with('ssh-')
323
+ expect("#{repo_path}/blah.der").to match_private_key("#{repo_path}/blah", 'hello')
324
+ end
325
+ end
326
+
327
+ context 'and a private_key resource pointing at it without a pass_phrase' do
328
+ with_recipe do
329
+ private_key "#{repo_path}/blah"
330
+ end
331
+
332
+ it 'the run fails with an exception' do
333
+ expect { chef_run }.to raise_error
334
+ end
335
+ end
336
+
337
+ context 'and a private_key resource with no pass phrase and regenerate_if_different' do
338
+ with_recipe do
339
+ private_key "#{repo_path}/blah" do
340
+ regenerate_if_different true
341
+ end
342
+ end
343
+
344
+ it 'the private_key is regenerated' do
345
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah]", :create
346
+ expect(IO.read("#{repo_path}/blah")).to start_with('-----BEGIN')
347
+ expect(OpenSSL::PKey.read(IO.read("#{repo_path}/blah"))).to be_kind_of(OpenSSL::PKey::RSA)
348
+ end
349
+ end
350
+
351
+ it 'a private_key resource that copies it from in-memory as a string succeeds' do
352
+ run_recipe do
353
+ private_key "#{repo_path}/blah.der" do
354
+ source_key IO.read("#{repo_path}/blah")
355
+ source_key_pass_phrase 'hello'
356
+ format :der
357
+ end
358
+ end
359
+
360
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah.der]", :create
361
+ key_str = IO.read("#{repo_path}/blah.der")
362
+ expect(key_str).not_to start_with('-----BEGIN')
363
+ expect(key_str).not_to start_with('ssh-')
364
+ expect("#{repo_path}/blah.der").to match_private_key("#{repo_path}/blah", 'hello')
365
+ end
366
+
367
+ context 'and a public_key' do
368
+ with_converge do
369
+ public_key "#{repo_path}/blah.pub" do
370
+ source_key_path "#{repo_path}/blah"
371
+ source_key_pass_phrase 'hello'
372
+ end
373
+ end
374
+
375
+ it 'the public_key is created in openssh format' do
376
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub]", :create
377
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
378
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah", 'hello'
379
+ end
380
+ end
381
+
382
+ context 'and a public_key derived from the private key in an in-memory string' do
383
+ with_converge do
384
+ public_key "#{repo_path}/blah.pub" do
385
+ source_key IO.read("#{repo_path}/blah")
386
+ source_key_pass_phrase 'hello'
387
+ end
388
+ end
389
+
390
+ it 'the public_key is created in openssh format' do
391
+ expect(chef_run).to have_updated "public_key[#{repo_path}/blah.pub]", :create
392
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
393
+ expect("#{repo_path}/blah.pub").to be_public_key_for "#{repo_path}/blah", 'hello'
394
+ end
395
+ end
396
+ end
397
+
398
+ context 'with a recipe with a private_key and public_key_path' do
399
+ with_converge do
400
+ private_key "#{repo_path}/blah" do
401
+ public_key_path "#{repo_path}/blah.pub"
402
+ end
403
+ end
404
+
405
+ it 'the private_key and public_key are created' do
406
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah]", :create
407
+ expect(IO.read("#{repo_path}/blah")).to start_with('-----BEGIN')
408
+ expect(OpenSSL::PKey.read(IO.read("#{repo_path}/blah"))).to be_kind_of(OpenSSL::PKey::RSA)
409
+ expect(IO.read("#{repo_path}/blah.pub")).to start_with('ssh-rsa ')
410
+ expect("#{repo_path}/blah.pub").to be_public_key_for("#{repo_path}/blah")
411
+ end
412
+ end
413
+
414
+ context 'with a recipe with a private_key and public_key_path and public_key_format' do
415
+ with_converge do
416
+ private_key "#{repo_path}/blah" do
417
+ public_key_path "#{repo_path}/blah.pub.der"
418
+ public_key_format :der
419
+ end
420
+ end
421
+
422
+ it 'the private_key and public_key are created' do
423
+ expect(chef_run).to have_updated "private_key[#{repo_path}/blah]", :create
424
+ expect(IO.read("#{repo_path}/blah")).to start_with('-----BEGIN')
425
+ expect(OpenSSL::PKey.read(IO.read("#{repo_path}/blah"))).to be_kind_of(OpenSSL::PKey::RSA)
426
+ expect(IO.read("#{repo_path}/blah.pub.der")).not_to start_with('ssh-rsa ')
427
+ expect("#{repo_path}/blah.pub.der").to be_public_key_for("#{repo_path}/blah")
428
+ end
429
+ end
430
+
431
+ context 'with a recipe with a private_key with path :none' do
432
+ it 'the private_key is created' do
433
+ got_private_key = nil
434
+ run_recipe do
435
+ private_key 'in_memory' do
436
+ path :none
437
+ after { |resource, private_key| got_private_key = private_key }
438
+ end
439
+ end
440
+
441
+ expect(chef_run).to have_updated "private_key[in_memory]", :create
442
+ expect(got_private_key).to be_kind_of(OpenSSL::PKey::RSA)
443
+ end
444
+ end
445
+
446
+ end