clc-promote 0.9.8 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ require 'promote'
2
+
3
+ describe Promote::EnvironmentFile do
4
+ let(:here) { File.expand_path('../../', __FILE__) }
5
+ let(:env_dir) { File.join(here, 'stubs', 'environments') }
6
+ let(:config) { Promote::Config.new(environment_directory: env_dir) }
7
+
8
+ subject { Promote::EnvironmentFile.new(environment, config) }
9
+
10
+ describe 'overrides' do
11
+ context 'there are overrides' do
12
+ let(:environment) { 'env1' }
13
+
14
+ it 'should return the override value' do
15
+ expect(subject.overrides['provisioner']['vault_bag']).to eq 'secrets_qa'
16
+ end
17
+ end
18
+
19
+ context 'there are no overrides' do
20
+ let(:environment) { 'env2' }
21
+
22
+ it 'should return an empty hash' do
23
+ expect(subject.overrides).to be_a(Hash)
24
+ expect(subject.overrides.keys.count).to be 0
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ require 'promote'
2
+
3
+ describe Promote::NodeShell::SshShell do
4
+ let(:ip) { '1.1.1.1' }
5
+ let(:node) { double('node', ipaddress: ip) }
6
+ let(:connection) { double('connection', open_channel: nil, loop: nil) }
7
+ let(:password) { 'pass' }
8
+ let(:vault) { double('vault', root_password: password) }
9
+
10
+ before do
11
+ allow(Promote::PasswordVault).to receive(:root_password)
12
+ .and_return(password)
13
+ end
14
+
15
+ subject { Promote::NodeShell::SshShell.new(node, vault) }
16
+
17
+ describe 'converge?' do
18
+ it 'converges the correct endpoint' do
19
+ expect(Net::SSH).to receive(:start).with(ip, anything, anything)
20
+ .and_return(connection)
21
+ subject.converge?
22
+ end
23
+
24
+ it 'converges as root' do
25
+ expect(Net::SSH).to receive(:start).with(anything, 'root', anything)
26
+ .and_return(connection)
27
+ subject.converge?
28
+ end
29
+
30
+ it 'converges with the correct password' do
31
+ expect(Net::SSH).to receive(:start).with(
32
+ anything,
33
+ anything,
34
+ password: password
35
+ ).and_return(connection)
36
+ subject.converge?
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ require 'promote'
2
+
3
+ describe Promote::NodeShell::WinrmShell do
4
+ let(:ip) { '1.1.1.1' }
5
+ let(:endpoint) { "http://#{ip}:5985/wsman" }
6
+ let(:node) { double('node', ipaddress: ip) }
7
+ let(:connection) { double('connection', run_cmd: { exitcode: 0 }, loop: nil) }
8
+ let(:password) { 'pass' }
9
+ let(:vault) { double('vault', admin_password: password) }
10
+
11
+ before do
12
+ allow(Promote::PasswordVault).to receive(:root_password)
13
+ .and_return(password)
14
+ end
15
+
16
+ subject { Promote::NodeShell::WinrmShell.new(node, vault) }
17
+
18
+ describe 'converge?' do
19
+ it 'converges with the correct authentication properties' do
20
+ expect(WinRM::WinRMWebService).to receive(:new).with(
21
+ endpoint,
22
+ :plaintext,
23
+ user: 'administrator',
24
+ pass: password,
25
+ basic_auth_only: true
26
+ ).and_return(connection)
27
+ subject.converge?
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ require 'promote'
2
+
3
+ describe Promote::NodeShell do
4
+ let(:config) do
5
+ Promote::Config.new(
6
+ node_name: 'user',
7
+ cookbook_directory: '/cookbooks',
8
+ client_key: 'key',
9
+ chef_server_url: 'https://some.chef.server'
10
+ )
11
+ end
12
+
13
+ subject { Promote::NodeShell }
14
+
15
+ describe 'for_node' do
16
+ let(:node) { double('node', platform_family: 'family') }
17
+
18
+ it 'adds the correct vault to the shell' do
19
+ expect(subject.for_node(node, config).vault.node).to be node
20
+ end
21
+
22
+ context 'linux node' do
23
+ let(:node) { double('node', platform_family: 'ubuntu') }
24
+
25
+ it 'returns a SshShell' do
26
+ expect(subject.for_node(node, config)).to(
27
+ be_instance_of Promote::NodeShell::SshShell)
28
+ end
29
+ end
30
+
31
+ context 'windows node' do
32
+ let(:node) { double('node', platform_family: 'windows') }
33
+
34
+ it 'returns a SshShell' do
35
+ expect(subject.for_node(node, config)).to(
36
+ be_instance_of Promote::NodeShell::WinrmShell)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ require 'promote'
2
+
3
+ describe Promote::PasswordVault do
4
+ let(:node) do
5
+ node = Chef::Node.new
6
+ node.name 'node'
7
+ node.override['provisioner']['vault_bag'] = bag
8
+ node.override['clc_library']['win_fqdn'] = domain_item
9
+ node
10
+ end
11
+ let(:bag) { 'secrets_qa' }
12
+ let(:domain_item) { 't3n' }
13
+ let(:config) { Promote::Config.new }
14
+ let(:vault) do
15
+ {
16
+ 'local_admin_password' => 'Pass@word1',
17
+ 'local_root_password' => 'Password1'
18
+ }
19
+ end
20
+
21
+ before do
22
+ allow(ChefVault::Item).to receive(:load)
23
+ .with(bag, "#{domain_item}_domain").and_return(vault)
24
+ end
25
+
26
+ subject { Promote::PasswordVault.new(node, config) }
27
+
28
+ describe 'root_password' do
29
+ it 'should return the correct password' do
30
+ expect(subject.root_password).to eq 'Password1'
31
+ end
32
+ end
33
+
34
+ describe 'admin_password' do
35
+ it 'should return the correct password' do
36
+ expect(subject.admin_password).to eq 'Pass@word1'
37
+ end
38
+ end
39
+ end
@@ -72,21 +72,27 @@ describe Promote::Promoter do
72
72
  let(:good_node) do
73
73
  node = Chef::Node.new
74
74
  node.name 'good_node'
75
- node.default['ohai_time'] = Time.now.to_i + 3600
75
+ node.default['ohai_time'] = Time.now.to_i - 1
76
76
  node
77
77
  end
78
78
  let(:bad_node) do
79
79
  node = Chef::Node.new
80
80
  node.name 'bad_node'
81
- node.default['ohai_time'] = Time.now.to_i - 3600
81
+ node.default['ohai_time'] = Time.now.to_i - 1
82
82
  node
83
83
  end
84
+ let(:good_shell) { double('shell', converge?: true) }
85
+ let(:bad_shell) { double('shell', converge?: false) }
84
86
  let(:uploader) { double('uploader') }
85
87
  let(:ui) { nil }
86
88
 
87
89
  before do
88
90
  allow(Promote::NodeFinder).to receive(:new).and_return(finder_good)
89
91
  allow(Promote::Uploader).to receive(:new).and_return(uploader)
92
+ allow(Promote::NodeShell).to receive(:for_node).with(good_node, config)
93
+ .and_return(good_shell)
94
+ allow(Promote::NodeShell).to receive(:for_node).with(bad_node, config)
95
+ .and_return(bad_shell)
90
96
  end
91
97
 
92
98
  context 'all environments succeed' do
@@ -98,9 +104,68 @@ describe Promote::Promoter do
98
104
  subject.monitor_promotion(
99
105
  'env1',
100
106
  %w(env2 env3 env4),
107
+ 3,
108
+ 0.25,
109
+ false,
110
+ ui
111
+ )
112
+ end
113
+ end
114
+
115
+ context 'converge windows first' do
116
+ let(:finder_ubu) { double('ubu_finder', search: [bad_node]) }
117
+ let(:finder_win) { double('win_finder', search: [good_node]) }
118
+
119
+ before do
120
+ allow(Promote::NodeFinder).to(
121
+ receive(:new).with(
122
+ 'chef_environment:env1 AND platform:ubuntu',
123
+ config
124
+ ).and_return(finder_ubu)
125
+ )
126
+ allow(Promote::NodeFinder).to(
127
+ receive(:new).with(
128
+ 'chef_environment:env1 AND platform:windows',
129
+ config
130
+ ).and_return(finder_win)
131
+ )
132
+ end
133
+
134
+ it 'promotes windows before ubuntu fails' do
135
+ expect(good_shell).to receive(:converge?)
136
+ expect do
137
+ subject.monitor_promotion(
138
+ 'env1',
139
+ %w(env2 env3 env4),
140
+ 0.25,
141
+ 3,
142
+ false,
143
+ ui
144
+ )
145
+ end.to raise_error(/env1/)
146
+ end
147
+ end
148
+
149
+ context 'dont converge if already converged' do
150
+ let(:good_node) do
151
+ node = Chef::Node.new
152
+ node.name 'converged'
153
+ node.default['ohai_time'] = Time.now.to_i + 3600
154
+ node
155
+ end
156
+
157
+ it 'does not converge' do
158
+ %w(env2 env3 env4).each do |env|
159
+ expect(subject).to receive(:promote_to).with('env1', env, ui)
160
+ expect(uploader).to receive(:upload_environment).with(env, ui)
161
+ end
162
+ expect(good_shell).not_to receive(:converge?)
163
+ subject.monitor_promotion(
164
+ 'env1',
165
+ %w(env2 env3 env4),
166
+ 3,
101
167
  0.25,
102
168
  false,
103
- 1,
104
169
  ui
105
170
  )
106
171
  end
@@ -110,77 +175,119 @@ describe Promote::Promoter do
110
175
  let(:self_node) do
111
176
  node = Chef::Node.new
112
177
  node.name config.node_name
113
- node.default['ohai_time'] = Time.now.to_i - 3600
178
+ node.default['ohai_time'] = Time.now.to_i - 1
114
179
  node
115
180
  end
116
- let(:finder_good) { double('node_finder', search: [self_node, good_node]) }
117
-
181
+ let(:finder_good) { double('finder', search: [self_node, good_node]) }
182
+ let(:self_shell) { double('shell') }
183
+
184
+ before do
185
+ allow(Promote::NodeShell).to receive(:for_node).with(self_node, config)
186
+ .and_return(self_shell)
187
+ end
188
+
118
189
  it 'promotes all nodes' do
119
190
  %w(env2 env3 env4).each do |env|
120
191
  expect(subject).to receive(:promote_to).with('env1', env, ui)
121
192
  expect(uploader).to receive(:upload_environment).with(env, ui)
122
193
  end
194
+ expect(self_shell).not_to receive(:converge?)
195
+ expect(good_shell).to receive(:converge?)
123
196
  subject.monitor_promotion(
124
197
  'env1',
125
198
  %w(env2 env3 env4),
199
+ 3,
126
200
  0.25,
127
201
  false,
128
- 1,
129
202
  ui
130
203
  )
131
204
  end
132
205
  end
133
206
 
134
- context 'source environment does not fully converge' do
135
- before do
136
- allow(Promote::NodeFinder).to(
137
- receive(:new).with('chef_environment:env1', config)
138
- .and_return(finder_bad)
139
- )
207
+ context 'ignore provisioner' do
208
+ let(:prov) do
209
+ node = Chef::Node.new
210
+ node.name 'BA1PROVISIONER01'
211
+ node.default['ohai_time'] = Time.now.to_i - 1
212
+ node
140
213
  end
214
+ let(:finder_good) { double('finder', search: [prov, good_node]) }
215
+ let(:prov_shell) { double('shell') }
141
216
 
142
- it 'promotes no nodes' do
143
- expect(subject).not_to receive(:promote_to)
144
- expect(uploader).not_to receive(:upload_environment)
217
+ before do
218
+ allow(Promote::NodeShell).to receive(:for_node).with(prov, config)
219
+ .and_return(prov_shell)
220
+ end
145
221
 
146
- expect do
147
- subject.monitor_promotion(
148
- 'env1',
149
- %w(env2 env3 env4),
150
- 0.25,
151
- false,
152
- 1,
153
- ui
154
- )
155
- end.to raise_error(/env1$/)
222
+ it 'promotes all nodes' do
223
+ %w(env2 env3 env4).each do |env|
224
+ expect(subject).to receive(:promote_to).with('env1', env, ui)
225
+ expect(uploader).to receive(:upload_environment).with(env, ui)
226
+ end
227
+ expect(prov_shell).not_to receive(:converge?)
228
+ expect(good_shell).to receive(:converge?)
229
+ subject.monitor_promotion(
230
+ 'env1',
231
+ %w(env2 env3 env4),
232
+ 3,
233
+ 0.25,
234
+ false,
235
+ ui
236
+ )
156
237
  end
157
238
  end
158
239
 
159
- context 'stops promotion when promotion fails' do
240
+ context 'converge fails' do
160
241
  before do
161
242
  allow(Promote::NodeFinder).to(
162
- receive(:new).with('chef_environment:env3', config)
163
- .and_return(finder_bad)
243
+ receive(:new).with(
244
+ "chef_environment:#{env} AND platform:ubuntu",
245
+ config
246
+ ).and_return(finder_bad)
164
247
  )
165
248
  end
166
249
 
167
- it 'stops on failure' do
168
- %w(env2 env3).each do |env|
169
- expect(subject).to receive(:promote_to).with('env1', env, ui)
170
- expect(uploader).to receive(:upload_environment).with(env, ui)
250
+ context 'source environment does not fully converge' do
251
+ let(:env) { 'env1' }
252
+
253
+ it 'promotes no nodes' do
254
+ expect(subject).not_to receive(:promote_to)
255
+ expect(uploader).not_to receive(:upload_environment)
256
+
257
+ expect do
258
+ subject.monitor_promotion(
259
+ 'env1',
260
+ %w(env2 env3 env4),
261
+ 3,
262
+ 0.25,
263
+ false,
264
+ ui
265
+ )
266
+ end.to raise_error(/env1/)
267
+ end
268
+ end
269
+
270
+ context 'stops promotion when promotion fails' do
271
+ let(:env) { 'env3' }
272
+
273
+ it 'stops on failure' do
274
+ %w(env2 env3).each do |env|
275
+ expect(subject).to receive(:promote_to).with('env1', env, ui)
276
+ expect(uploader).to receive(:upload_environment).with(env, ui)
277
+ end
278
+ expect(subject).not_to receive(:promote_to).with('env1', 'env4', ui)
279
+ expect(uploader).not_to receive(:upload_environment).with('env4')
280
+ expect do
281
+ subject.monitor_promotion(
282
+ 'env1',
283
+ %w(env2 env3 env4),
284
+ 3,
285
+ 0.25,
286
+ false,
287
+ ui
288
+ )
289
+ end.to raise_error(/env3/)
171
290
  end
172
- expect(subject).not_to receive(:promote_to).with('env1', 'env4', ui)
173
- expect(uploader).not_to receive(:upload_environment).with('env4')
174
- expect do
175
- subject.monitor_promotion(
176
- 'env1',
177
- %w(env2 env3 env4),
178
- 0.25,
179
- false,
180
- 1,
181
- ui
182
- )
183
- end.to raise_error(/env3$/)
184
291
  end
185
292
  end
186
293
  end
@@ -187,8 +187,18 @@ describe Promote::Uploader do
187
187
  let(:env_name) { 'my_test' }
188
188
 
189
189
  it 'uploads the environment' do
190
- expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(an_instance_of(Chef::ChefFS::FilePattern), anything(), anything(), anything(), anything(), anything(), anything()) do |arg|
191
- expect(arg.pattern).to eq(File.join('/environments', "#{env_name}.json"))
190
+ expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(
191
+ an_instance_of(Chef::ChefFS::FilePattern),
192
+ anything,
193
+ anything,
194
+ anything,
195
+ anything,
196
+ anything,
197
+ anything
198
+ ) do |arg|
199
+ expect(arg.pattern).to eq(
200
+ File.join('/environments', "#{env_name}.json")
201
+ )
192
202
  end
193
203
  subject.upload_environment(env_name)
194
204
  end
@@ -196,19 +206,20 @@ describe Promote::Uploader do
196
206
 
197
207
  context 'upload all environments' do
198
208
  it 'uploads the environment' do
199
- local_environments = Chef::ChefFS::Config.new.local_fs.child_paths['environments']
209
+ local = Chef::ChefFS::Config.new.local_fs.child_paths['environments']
200
210
 
201
211
  expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(
202
212
  an_instance_of(Chef::ChefFS::FilePattern),
203
- an_instance_of(Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir),
204
- anything(),
205
- anything(),
206
- anything(),
207
- anything(),
208
- anything()
213
+ an_instance_of(
214
+ Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir),
215
+ anything,
216
+ anything,
217
+ anything,
218
+ anything,
219
+ anything
209
220
  ) do |file_pattern, source|
210
221
  expect(file_pattern.pattern).to eq(File.join('/environments/*.json'))
211
- expect(source.child_paths['environments']).to eq(local_environments)
222
+ expect(source.child_paths['environments']).to eq(local)
212
223
  end
213
224
  subject.upload_environments
214
225
  end
@@ -219,13 +230,14 @@ describe Promote::Uploader do
219
230
  local_roles = Chef::ChefFS::Config.new.local_fs.child_paths['roles']
220
231
 
221
232
  expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(
222
- an_instance_of(Chef::ChefFS::FilePattern),
223
- an_instance_of(Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir),
224
- anything(),
225
- anything(),
226
- anything(),
227
- anything(),
228
- anything()
233
+ an_instance_of(Chef::ChefFS::FilePattern),
234
+ an_instance_of(
235
+ Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir),
236
+ anything,
237
+ anything,
238
+ anything,
239
+ anything,
240
+ anything
229
241
  ) do |file_pattern, source|
230
242
  expect(file_pattern.pattern).to eq(File.join('/roles/*.json'))
231
243
  expect(source.child_paths['roles']).to eq(local_roles)
@@ -238,24 +250,27 @@ describe Promote::Uploader do
238
250
  let(:temp_data_bags) { File.join(config.temp_directory, 'data_bags') }
239
251
  let(:fake_bag_file) { File.join('bags', 'bag.json') }
240
252
  let(:secret_key_file) { File.join('bags', 'bag_secrets_keys.json') }
241
- let(:config) { Promote::Config.new({
242
- :temp_directory => '/tmp/promote_tests',
243
- :repo_root => '/tmp/promote_repo',
244
- :node_name => 'user',
245
- :client_key => 'key',
246
- :chef_server_url => 'https://some.chef.server'}) }
253
+ let(:config) do
254
+ Promote::Config.new(
255
+ temp_directory: '/tmp/promote_tests',
256
+ repo_root: '/tmp/promote_repo',
257
+ node_name: 'user',
258
+ client_key: 'key',
259
+ chef_server_url: 'https://some.chef.server'
260
+ )
261
+ end
247
262
 
248
- before {
263
+ before do
249
264
  fake_bag_path = File.join(config.data_bag_directory, fake_bag_file)
250
265
  FileUtils.mkdir_p(File.dirname(fake_bag_path))
251
266
  FileUtils.touch(fake_bag_path)
252
267
  FileUtils.touch(File.join(config.data_bag_directory, secret_key_file))
253
- }
268
+ end
254
269
 
255
- after {
270
+ after do
256
271
  FileUtils.rm_rf(config.temp_directory)
257
272
  FileUtils.rm_rf(config.repo_root)
258
- }
273
+ end
259
274
 
260
275
  it 'copies the data_bags to a temp folder' do
261
276
  subject.upload_data_bags
@@ -272,12 +287,13 @@ describe Promote::Uploader do
272
287
  it 'uploads the data_bags to chef' do
273
288
  expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(
274
289
  an_instance_of(Chef::ChefFS::FilePattern),
275
- an_instance_of(Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir),
276
- anything(),
277
- anything(),
278
- anything(),
279
- anything(),
280
- anything()
290
+ an_instance_of(
291
+ Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir),
292
+ anything,
293
+ anything,
294
+ anything,
295
+ anything,
296
+ anything
281
297
  ) do |file_pattern, source|
282
298
  expect(file_pattern.pattern).to eq('/data_bags/**/*.json')
283
299
  expect(source.child_paths['data_bags']).to eq([temp_data_bags])