clc-promote 0.9.8 → 0.10.0

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.
@@ -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])