clc-promote 0.4.5 → 0.7.8

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.
@@ -1,12 +1,15 @@
1
1
  require 'promote'
2
2
 
3
3
  describe Promote::Promoter do
4
- context "Promote one environment to another" do
5
- let(:config) { Promote::Config.new({
6
- :node_name => 'user',
7
- :cookbook_directory => '/cookbooks',
8
- :client_key => 'key',
9
- :chef_server_url => 'https://some.chef.server'}) }
4
+ let(:config) { Promote::Config.new({
5
+ :node_name => 'user',
6
+ :cookbook_directory => '/cookbooks',
7
+ :client_key => 'key',
8
+ :chef_server_url => 'https://some.chef.server'}) }
9
+
10
+ subject { Promote::Promoter.new(config) }
11
+
12
+ context "promote_to" do
10
13
  let(:fake_file){double('file')}
11
14
  let(:env1) do
12
15
  <<-EOS
@@ -43,8 +46,6 @@ describe Promote::Promoter do
43
46
  allow(File).to receive(:open).with(/env2\.json$/, "w").and_yield(fake_file)
44
47
  }
45
48
 
46
- subject { Promote::Promoter.new(config) }
47
-
48
49
  it "copies the cookbook constraints" do
49
50
  expect(fake_file).to receive(:<<).with(an_instance_of(String)) do |arg|
50
51
  parsed = JSON.parse(arg)
@@ -56,4 +57,46 @@ describe Promote::Promoter do
56
57
  subject.promote_to("env1", "env2")
57
58
  end
58
59
  end
60
+
61
+ context "monitor_promotion" do
62
+ let(:finder_good){ double('node_finder', :search => [good_node]) }
63
+ let(:finder_bad){ double('node_finder', :search => [bad_node]) }
64
+ let (:good_node) {
65
+ node = Chef::Node.new
66
+ node.default["ohai_time"] = Time.now.to_i + 3600
67
+ node
68
+ }
69
+ let (:bad_node) {
70
+ node = Chef::Node.new
71
+ node.default["ohai_time"] = Time.now.to_i - 3600
72
+ node
73
+ }
74
+
75
+ before {
76
+ allow(Promote::NodeFinder).to receive(:new).and_return(finder_good)
77
+ }
78
+
79
+ context "all environments succeed" do
80
+ it "promotes all nodes" do
81
+ expect(subject).to receive(:promote_to).with("env1", "env2", nil)
82
+ expect(subject).to receive(:promote_to).with("env1", "env3", nil)
83
+ expect(subject).to receive(:promote_to).with("env1", "env4", nil)
84
+ subject.monitor_promotion("env1", ["env2", "env3", "env4"], 1)
85
+ end
86
+ end
87
+
88
+ context "stops promotion when promotion fails" do
89
+ before {
90
+ allow(Promote::NodeFinder).to receive(:new).with("chef_environment:env3", config).and_return(finder_bad)
91
+ }
92
+
93
+ it "stops on failure" do
94
+
95
+ expect(subject).to receive(:promote_to).with("env1", "env2", nil)
96
+ expect(subject).not_to receive(:promote_to).with("env1", "env3", nil)
97
+ expect(subject).not_to receive(:promote_to).with("env1", "env4", nil)
98
+ subject.monitor_promotion("env1", ["env2", "env3", "env4"], 1)
99
+ end
100
+ end
101
+ end
59
102
  end
@@ -4,15 +4,19 @@ require_relative '../support/dummy_metadata'
4
4
  describe Promote::Uploader do
5
5
  let(:knife_config) {{ :cookbook_path => "path"}}
6
6
  let(:temp_dir) { "/tmp/berks" }
7
- let(:config) { Promote::Config.new({
8
- :node_name => 'user',
7
+ let(:config) { Promote::Config.new({
8
+ :node_name => 'user',
9
9
  :cookbook_directory => '/cookbooks',
10
- :client_key => 'key',
10
+ :client_key => 'key',
11
11
  :temp_directory => temp_dir,
12
12
  :chef_server_url => 'https://some.chef.server'}) }
13
13
 
14
14
  subject { Promote::Uploader.new(config) }
15
15
 
16
+ before {
17
+ allow(Chef::ChefFS::FileSystem).to receive(:copy_to)
18
+ }
19
+
16
20
  context "upload cookbooks" do
17
21
  let(:env_name) { "QA1" }
18
22
  let(:metadata) { PromoteSpecs::DummyMetadata.new({'version' => "1.1.1"}) }
@@ -50,6 +54,7 @@ describe Promote::Uploader do
50
54
  }}
51
55
  let(:rest){double('rest')}
52
56
  let(:knife) { instance_double('CookbookUpload', :run => nil) }
57
+
53
58
  before {
54
59
  allow(File).to receive(:read).with(File.join(config.environment_directory, "#{env_name}.json")).and_return(environment)
55
60
  allow(File).to receive(:read).with(/metadata\.json$/).and_return('')
@@ -59,6 +64,7 @@ describe Promote::Uploader do
59
64
  allow(Chef::Knife::CookbookUpload).to receive(:new).and_return(knife)
60
65
  allow(Dir).to receive(:glob).with(File.expand_path("~/.berkshelf/cookbooks/*")).and_return(berks_cookbooks)
61
66
  allow(Dir).to receive(:glob).with(File.join(config.cookbook_directory, "*")).and_return(internal_cookbooks)
67
+ allow(Dir).to receive(:glob).with(File.join(config.temp_directory, "*")).and_return(internal_cookbooks)
62
68
  allow(Dir).to receive(:mkdir)
63
69
  allow(Dir).to receive(:exist?).and_return(false)
64
70
  allow(knife).to receive(:config).and_return(knife_config)
@@ -117,7 +123,7 @@ describe Promote::Uploader do
117
123
  context "temp directory already exists" do
118
124
  before {allow(Dir).to receive(:exist?).and_return(true)}
119
125
 
120
- it "deletes temp directory" do
126
+ it "deletes temp directory" do
121
127
  expect(FileUtils).to receive(:rm_rf).with(config.temp_directory)
122
128
  subject.upload_cookbooks(env_name)
123
129
  end
@@ -126,7 +132,7 @@ describe Promote::Uploader do
126
132
  context "no metadata.rb file exists for cookbook" do
127
133
  before {allow(File).to receive(:exist?).with(/rb$/).and_return(false)}
128
134
 
129
- it "loads json instead of ruby file" do
135
+ it "loads json instead of ruby file" do
130
136
  expect(metadata).to receive(:from_json).at_least(1).times
131
137
  expect(metadata).not_to receive(:from_file)
132
138
  subject.upload_cookbooks(env_name)
@@ -137,7 +143,7 @@ describe Promote::Uploader do
137
143
  let(:berks_cookbooks) {[]}
138
144
  let(:internal_cookbooks) {[]}
139
145
 
140
- it "does not call knife" do
146
+ it "does not call knife" do
141
147
  expect(Chef::Knife::CookbookUpload).not_to receive(:new)
142
148
 
143
149
  subject.upload_cookbooks(env_name)
@@ -145,31 +151,31 @@ describe Promote::Uploader do
145
151
  end
146
152
 
147
153
  context "config missing node_name" do
148
- let(:config) { Promote::Config.new({
149
- :client_key => 'key',
154
+ let(:config) { Promote::Config.new({
155
+ :client_key => 'key',
150
156
  :chef_server_url => 'https://some.chef.server'}) }
151
157
 
152
- it "raises error that node_name is missing" do
158
+ it "raises error that node_name is missing" do
153
159
  expect{subject.upload_cookbooks(env_name)}.to raise_error(/node_name/)
154
160
  end
155
161
  end
156
162
 
157
163
  context "config missing client_key" do
158
- let(:config) { Promote::Config.new({
159
- :node_name => 'user',
164
+ let(:config) { Promote::Config.new({
165
+ :node_name => 'user',
160
166
  :chef_server_url => 'https://some.chef.server'}) }
161
167
 
162
- it "raises error that client_key is missing" do
168
+ it "raises error that client_key is missing" do
163
169
  expect{subject.upload_cookbooks(env_name)}.to raise_error(/client_key/)
164
170
  end
165
171
  end
166
172
 
167
173
  context "config missing chef_server_url" do
168
- let(:config) { Promote::Config.new({
169
- :node_name => 'user',
174
+ let(:config) { Promote::Config.new({
175
+ :node_name => 'user',
170
176
  :client_key => 'key'}) }
171
177
 
172
- it "raises error that chef_server_url is missing" do
178
+ it "raises error that chef_server_url is missing" do
173
179
  expect{subject.upload_cookbooks(env_name)}.to raise_error(/chef_server_url/)
174
180
  end
175
181
  end
@@ -178,7 +184,7 @@ describe Promote::Uploader do
178
184
  context "upload json artifacts" do
179
185
  context "upload an environment" do
180
186
  let(:env_name) { "my_test" }
181
-
187
+
182
188
  it "uploads the environment" do
183
189
  expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(an_instance_of(Chef::ChefFS::FilePattern), anything(), anything(), anything(), anything()) do |arg|
184
190
  expect(arg.pattern).to eq(File.join("/environments", "#{env_name}.json"))
@@ -189,18 +195,87 @@ describe Promote::Uploader do
189
195
 
190
196
  context "upload all environments" do
191
197
  it "uploads the environment" do
192
- expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(an_instance_of(Chef::ChefFS::FilePattern), anything(), anything(), anything(), anything()) do |arg|
193
- expect(arg.pattern).to eq(File.join("/environments/*.json"))
198
+ local_environments = Chef::ChefFS::Config.new.local_fs.child_paths["environments"]
199
+
200
+ expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(
201
+ an_instance_of(Chef::ChefFS::FilePattern),
202
+ an_instance_of(Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir),
203
+ anything(),
204
+ anything(),
205
+ anything()
206
+ ) do |file_pattern, source|
207
+ expect(file_pattern.pattern).to eq(File.join("/environments/*.json"))
208
+ expect(source.child_paths["environments"]).to eq(local_environments)
194
209
  end
195
210
  subject.upload_environments
196
211
  end
197
212
  end
198
213
 
199
- context "upload data_bags" do
200
- it "uploads the data_bags" do
201
- expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(an_instance_of(Chef::ChefFS::FilePattern), anything(), anything(), anything(), anything()) do |arg|
202
- expect(arg.pattern).to eq("/data_bags/**/*.json")
214
+ context "upload all roles" do
215
+ it "uploads the role" do
216
+ local_roles = Chef::ChefFS::Config.new.local_fs.child_paths["roles"]
217
+
218
+ expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(
219
+ an_instance_of(Chef::ChefFS::FilePattern),
220
+ an_instance_of(Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir),
221
+ anything(),
222
+ anything(),
223
+ anything()
224
+ ) do |file_pattern, source|
225
+ expect(file_pattern.pattern).to eq(File.join("/roles/*.json"))
226
+ expect(source.child_paths["roles"]).to eq(local_roles)
227
+ end
228
+ subject.upload_roles
229
+ end
230
+ end
231
+
232
+ context "upload_data_bags" do
233
+ let(:temp_data_bags) { File.join(config.temp_directory, "data_bags") }
234
+ let(:fake_bag_file) { File.join("bags", "bag.json") }
235
+ let(:secret_key_file) { File.join("bags", "bag_secrets_keys.json") }
236
+ let(:config) { Promote::Config.new({
237
+ :temp_directory => "/tmp/promote_tests",
238
+ :repo_root => "/tmp/promote_repo",
239
+ :node_name => 'user',
240
+ :client_key => 'key',
241
+ :chef_server_url => 'https://some.chef.server'}) }
242
+
243
+ before {
244
+ fake_bag_path = File.join(config.data_bag_directory, fake_bag_file)
245
+ FileUtils.mkdir_p(File.dirname(fake_bag_path))
246
+ FileUtils.touch(fake_bag_path)
247
+ FileUtils.touch(File.join(config.data_bag_directory, secret_key_file))
248
+ }
249
+
250
+ after {
251
+ FileUtils.rm_rf(config.temp_directory)
252
+ FileUtils.rm_rf(config.repo_root)
253
+ }
254
+
255
+ it "copies the data_bags to a temp folder" do
256
+ subject.upload_data_bags
257
+
258
+ expect(File).to exist(File.join(temp_data_bags, fake_bag_file))
259
+ end
260
+
261
+ it "does not copy key files" do
262
+ subject.upload_data_bags
263
+
264
+ expect(File).not_to exist(File.join(temp_data_bags, secret_key_file))
265
+ end
266
+
267
+ it "uploads the data_bags to chef" do
268
+ expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(
269
+ an_instance_of(Chef::ChefFS::FilePattern),
270
+ an_instance_of(Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir),
271
+ anything(),
272
+ anything(),
273
+ anything()
274
+ ) do |file_pattern, source|
275
+ expect(file_pattern.pattern).to eq("/data_bags/**/*.json")
276
+ expect(source.child_paths["data_bags"]).to eq([temp_data_bags])
203
277
  end
278
+
204
279
  subject.upload_data_bags
205
280
  end
206
281
  end
@@ -57,8 +57,8 @@ describe Promote::Versioner do
57
57
 
58
58
  context 'version_cookbooks' do
59
59
  let(:cookbooks) {[
60
- File.join(subject.config.cookbook_directory, "cookbook1"),
61
- File.join(subject.config.cookbook_directory, "cookbook2"),
60
+ File.join(subject.config.cookbook_directory, "cookbook1"),
61
+ File.join(subject.config.cookbook_directory, "cookbook2"),
62
62
  File.join(subject.config.cookbook_directory, "cookbook3")
63
63
  ]}
64
64
  before {
@@ -171,6 +171,120 @@ describe Promote::Versioner do
171
171
  end
172
172
  end
173
173
 
174
+ context 'version_role' do
175
+ before {
176
+ regex = File.join(config.role_directory,'test.json')
177
+ allow(File).to receive(:read).with(regex).and_return(artifact_file)
178
+ allow(File).to receive(:open).with(regex, "w").and_yield(fake_file)
179
+ }
180
+
181
+ context 'when versioning a new role with no version' do
182
+ let(:artifact_file) do
183
+ <<-EOS
184
+ {
185
+ "name": "QA1",
186
+ "chef_type": "role",
187
+ "json_class": "Chef::Role",
188
+ "override_attributes": {
189
+ "foo": "bar"
190
+ }
191
+ }
192
+ EOS
193
+ end
194
+ it "writes the new version and sha to the file" do
195
+ parsed = JSON.parse(artifact_file)
196
+ parsed['override_attributes']['version'] = '1.2.2'
197
+ parsed['override_attributes']['sha1'] = 'aaa'
198
+ expect(fake_file).to receive(:<<).with(JSON.pretty_generate(parsed))
199
+ subject.version_role('test')
200
+ end
201
+ end
202
+
203
+ context 'when versioning a role with no override_attributes' do
204
+ let(:artifact_file) do
205
+ <<-EOS
206
+ {
207
+ "name": "QA1",
208
+ "chef_type": "role",
209
+ "json_class": "Chef::Role"
210
+ }
211
+ EOS
212
+ end
213
+ it "adds an override_attributes key to the role" do
214
+ parsed = JSON.parse(artifact_file)
215
+ override_hash = { "override_attributes" => {} }
216
+ parsed.merge!(override_hash)
217
+ expect(fake_file).to receive(:<<).with(JSON.pretty_generate(parsed))
218
+ subject.version_role('test')
219
+ end
220
+ end
221
+
222
+ context 'when versioning a new role with old version' do
223
+
224
+ let(:artifact_file) do
225
+ <<-EOS
226
+ {
227
+ "name": "QA1",
228
+ "chef_type": "role",
229
+ "json_class": "Chef::Role",
230
+ "override_attributes": {
231
+ "foo": "bar",
232
+ "version": "1.0.0",
233
+ "sha1": "ccc"
234
+ }
235
+ }
236
+ EOS
237
+ end
238
+
239
+ it "writes the new version and sha to the file" do
240
+ parsed = JSON.parse(artifact_file)
241
+ parsed['override_attributes']['version'] = '1.2.2'
242
+ parsed['override_attributes']['sha1'] = 'aaa'
243
+ expect(fake_file).to receive(:<<).with(JSON.pretty_generate(parsed))
244
+ subject.version_role('test')
245
+ end
246
+ end
247
+
248
+ context 'when committing a role with no changes' do
249
+ let(:artifact_file) do
250
+ <<-EOS
251
+ {
252
+ "name": "QA1",
253
+ "chef_type": "role",
254
+ "json_class": "Chef::Role",
255
+ "override_attributes": {
256
+ "foo": "bar",
257
+ "version": "1.2.2",
258
+ "sha1": "aaa"
259
+ }
260
+ }
261
+ EOS
262
+ end
263
+
264
+ it "does not write to the file" do
265
+ expect(fake_file).not_to receive(:<<)
266
+ subject.version_role('test')
267
+ end
268
+ end
269
+ end
270
+
271
+ context 'version_roles' do
272
+ let(:roles) {%w{dir/role1.json dir/role2.json dir/role3.json}}
273
+ before {
274
+ allow(subject).to receive(:version_role)
275
+ allow(Dir).to receive(:glob).with(
276
+ File.join(subject.config.role_directory, "*.json")).and_return(roles)
277
+ }
278
+
279
+ it "versions each role" do
280
+ roles.each do |role|
281
+ expect(subject).to receive(:version_role).with(
282
+ File.basename(role ,File.extname(role)))
283
+ end
284
+ subject.version_roles
285
+ end
286
+ end
287
+
174
288
  context 'constrain_environment' do
175
289
  before {
176
290
  regex = File.join(config.environment_directory,'test.json')
@@ -232,59 +346,80 @@ describe Promote::Versioner do
232
346
  let(:artifact_file) do
233
347
  <<-EOS
234
348
  {
235
- "name": "QA1",
349
+ "name": "LKG",
350
+ "chef_type": "environment",
351
+ "json_class": "Chef::Environment",
352
+ "cookbook_versions": {
353
+ "cookbook1": "1.1.1.hash",
354
+ "cookbook2": "1.1.1.hash",
355
+ "cookbook3": "3.3.3.hash"
356
+ }
357
+ }
358
+ EOS
359
+ end
360
+
361
+ it "returns dirty" do
362
+ expect(subject.is_dirty('new', 'test', 'cookbook2', 'hash')).to be(true)
363
+ end
364
+ end
365
+ context "cookbook has same version but different hash" do
366
+
367
+ let(:artifact_file) do
368
+ <<-EOS
369
+ {
370
+ "name": "LKG",
236
371
  "chef_type": "environment",
237
372
  "json_class": "Chef::Environment",
238
373
  "cookbook_versions": {
239
- "cookbook1": "1.1.1",
240
- "cookbook2": "1.1.1",
241
- "cookbook3": "3.3.3"
374
+ "cookbook1": "1.1.1.hash",
375
+ "cookbook2": "2.2.2.diff_hash",
376
+ "cookbook3": "3.3.3.hash"
242
377
  }
243
378
  }
244
379
  EOS
245
380
  end
246
381
 
247
382
  it "returns dirty" do
248
- expect(subject.is_dirty('new', 'test', 'cookbook2')).to be(true)
383
+ expect(subject.is_dirty('new', 'test', 'cookbook2', 'hash')).to be(true)
249
384
  end
250
385
  end
251
386
  context "cookbook is not dirty" do
252
387
  let(:artifact_file) do
253
388
  <<-EOS
254
389
  {
255
- "name": "QA1",
390
+ "name": "LKG",
256
391
  "chef_type": "environment",
257
392
  "json_class": "Chef::Environment",
258
393
  "cookbook_versions": {
259
- "cookbook1": "1.1.1",
260
- "cookbook2": "2.2.2",
261
- "cookbook3": "3.3.3"
394
+ "cookbook1": "1.1.1.hash",
395
+ "cookbook2": "2.2.2.hash",
396
+ "cookbook3": "3.3.3.hash"
262
397
  }
263
398
  }
264
399
  EOS
265
400
  end
266
401
 
267
402
  it "returns clean" do
268
- expect(subject.is_dirty('new', 'test', 'cookbook2')).to be(false)
403
+ expect(subject.is_dirty('new', 'test', 'cookbook2', 'hash')).to be(false)
269
404
  end
270
405
  end
271
406
  context "cookbook is new to environment" do
272
407
  let(:artifact_file) do
273
408
  <<-EOS
274
409
  {
275
- "name": "QA1",
410
+ "name": "LKG",
276
411
  "chef_type": "environment",
277
412
  "json_class": "Chef::Environment",
278
413
  "cookbook_versions": {
279
- "cookbook1": "1.1.1",
280
- "cookbook3": "3.3.3"
414
+ "cookbook1": "1.1.1.hash",
415
+ "cookbook3": "3.3.3.hash"
281
416
  }
282
417
  }
283
418
  EOS
284
419
  end
285
420
 
286
421
  it "returns dirty" do
287
- expect(subject.is_dirty('new', 'test', 'cookbook2')).to be(true)
422
+ expect(subject.is_dirty('new', 'test', 'cookbook2', 'hash')).to be(true)
288
423
  end
289
424
  end
290
425
  end