clc-cheffish 0.8.clc

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.
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