knife-ec-backup 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5c0eaeaca5c1a8707e66a26cafe08186a77ea6fa
4
- data.tar.gz: 2b4675644bc5a526f3a26ad436dfd73a30ed1307
3
+ metadata.gz: 090c5b339d237877732a3d620eb92a4466addb75
4
+ data.tar.gz: 76cbbc8bb669fd36a722515b618838d3a100603b
5
5
  SHA512:
6
- metadata.gz: ba31ebcf31a0aca8b4df975c9687436822c5fabdc81095b23e30c4e60e35f7ff88516334949ad53fa86c4d8fe4c6787c1ed5764fa9ddd0057e132ee73f78eab1
7
- data.tar.gz: e751b01f0094618a3fb5a6249ef083a42d20e911135d69d07b9bba77a4438b4118fb1f7f80201ec86ec7cd392865f1d8241775c8b4f9e83305ad9fbf3d6871e8
6
+ metadata.gz: d074dfc01e8be51da10c38831d02aeff7b3aa8cd672c93540f9e53cbc161bc4c8ba5b97be95ef5a978d131aa0f5b02883a77243f7ef34bc5e77df440eab2b7ba
7
+ data.tar.gz: b092806e811fa6b550b145d42c2ac55ab543281eafaf46c53f68118d1f8e498e2fd4196af48252d7a40105f7947e13638cca334a29dd56457d27501094aaa1fa
@@ -77,13 +77,21 @@ class Chef
77
77
  remote_users.each_pair do |name, url|
78
78
  yield name, url
79
79
  end
80
+ rescue Net::HTTPServerException => ex
81
+ knife_ec_error_handler.add(ex)
80
82
  end
81
83
 
82
84
  def for_each_organization
83
85
  rest.get('/organizations').each_pair do |name, url|
84
86
  next unless (config[:org].nil? || config[:org] == name)
85
87
  ui.msg "Downloading organization object for #{name} from #{url}"
86
- org = rest.get(url)
88
+ begin
89
+ org = rest.get(url)
90
+ rescue Net::HTTPServerException => ex
91
+ ui.error "Failed to find organization '#{name}'."
92
+ knife_ec_error_handler.add(ex)
93
+ next
94
+ end
87
95
  # Enterprise Chef 11 and below uses a pool of precreated
88
96
  # organizations to account for slow organization creation
89
97
  # using CouchDB. Thus, on server versions < 12 we want to
@@ -102,12 +110,16 @@ class Chef
102
110
  File.open("#{dest_dir}/users/#{username}.json", 'w') do |file|
103
111
  file.write(Chef::JSONCompat.to_json_pretty(rest.get(url)))
104
112
  end
113
+ rescue Net::HTTPServerException => ex
114
+ knife_ec_error_handler.add(ex)
105
115
  end
106
116
 
107
117
  def download_user_acl(username)
108
118
  File.open("#{dest_dir}/user_acls/#{username}.json", 'w') do |file|
109
119
  file.write(Chef::JSONCompat.to_json_pretty(user_acl_rest.get("users/#{username}/_acl")))
110
120
  end
121
+ rescue Net::HTTPServerException => ex
122
+ knife_ec_error_handler.add(ex)
111
123
  end
112
124
 
113
125
  def export_from_sql
@@ -137,6 +149,8 @@ class Chef
137
149
  File.open("#{dest_dir}/organizations/#{name}/members.json", 'w') do |file|
138
150
  file.write(Chef::JSONCompat.to_json_pretty(rest.get("/organizations/#{name}/users")))
139
151
  end
152
+ rescue Net::HTTPServerException => ex
153
+ knife_ec_error_handler.add(ex)
140
154
  end
141
155
 
142
156
  def download_org_invitations(name)
@@ -144,6 +158,8 @@ class Chef
144
158
  File.open("#{dest_dir}/organizations/#{name}/invitations.json", 'w') do |file|
145
159
  file.write(Chef::JSONCompat.to_json_pretty(rest.get("/organizations/#{name}/association_requests")))
146
160
  end
161
+ rescue Net::HTTPServerException => ex
162
+ knife_ec_error_handler.add(ex)
147
163
  end
148
164
 
149
165
  def ensure_dir(dir)
@@ -225,6 +241,8 @@ class Chef
225
241
  chef_fs_config.local_fs, nil,
226
242
  config, ui,
227
243
  proc { |entry| chef_fs_config.format_path(entry) })
244
+ rescue Net::HTTPServerException => ex
245
+ knife_ec_error_handler.add(ex)
228
246
  end
229
247
  end
230
248
  end
@@ -19,6 +19,7 @@
19
19
  require 'chef/knife'
20
20
  require 'chef/server_api'
21
21
  require 'veil'
22
+ require 'chef/knife/ec_error_handler'
22
23
 
23
24
  class Chef
24
25
  class Knife
@@ -115,6 +116,8 @@ class Chef
115
116
  else
116
117
  admin_users[0]
117
118
  end
119
+ rescue Net::HTTPServerException => ex
120
+ knife_ec_error_handler.add(ex)
118
121
  end
119
122
  end
120
123
 
@@ -150,6 +153,10 @@ class Chef
150
153
  raise Chef::Knife::EcBase::UnImplemented
151
154
  end
152
155
 
156
+ def knife_ec_error_handler
157
+ @knife_ec_error_handler ||= Chef::Knife::EcErrorHandler.new(dest_dir, self.class)
158
+ end
159
+
153
160
  def user_acl_rest
154
161
  @user_acl_rest ||= if config[:skip_version]
155
162
  rest
@@ -0,0 +1,114 @@
1
+ # Author:: Jeremy Miller (<jmiller@chef.io>)
2
+ # Copyright:: Copyright (c) 2017 Chef Software, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ class Chef
18
+ class Knife
19
+ # This class handles the errors that we might encounter on a
20
+ # backup or restore, it stors the errors inside a specific file
21
+ # inside the working directory.
22
+ class EcErrorHandler
23
+
24
+ attr_reader :err_file
25
+
26
+ # Creates a new instance of the EcErrorHandler to start
27
+ # adding errors during a backup or restore.
28
+ def initialize(working_dir, process)
29
+ @err_dir = "#{working_dir}/errors"
30
+ FileUtils.mkdir_p(@err_dir)
31
+
32
+ # Create an specific error file name depending
33
+ # of where the process comes from.
34
+ @err_file = if process == Chef::Knife::EcRestore
35
+ File.join(@err_dir, "restore-#{Time.now.iso8601}.json")
36
+ elsif process == Chef::Knife::EcBackup
37
+ File.join(@err_dir, "backup-#{Time.now.iso8601}.json")
38
+ else
39
+ File.join(@err_dir, "other-#{Time.now.iso8601}.json")
40
+ end
41
+
42
+ # exit handler
43
+ at_exit { display(@err_file) }
44
+ end
45
+
46
+ # Add an exception to the error file.
47
+ #
48
+ # For now we are writing all the errors to a single file, but
49
+ # in the future we would like to be able to generate a full path
50
+ # just as we do when we are backing up the Server, just that putting
51
+ # the file inside `{work_dir}/errors/{path}`
52
+ #
53
+ # Example:
54
+ # A failed user
55
+ # => {work_dir}/errors/users/afiune.json
56
+ # A failed cookbook
57
+ # => {work_dir}/errors/cookbooks/burger.json
58
+ # A failed environment
59
+ # => {work_dir}/errors/environment/dev.json
60
+ #
61
+ # The advantages of this schema is the ability to retry the backup or
62
+ # restore and pick up where we left.
63
+ def add(ex)
64
+ msg = {
65
+ timestamp: Time.now,
66
+ message: ex.message,
67
+ backtrace: ex.backtrace,
68
+ exception: ex.class
69
+ }
70
+
71
+ if ex.respond_to?(:chef_rest_request=) && ex.chef_rest_request
72
+ msg.merge!( {
73
+ req_path: ex.chef_rest_request.path,
74
+ req_method: ex.chef_rest_request.method
75
+ })
76
+ end
77
+
78
+ lock_file(@err_file, 'a') do |f|
79
+ f.write(Chef::JSONCompat.to_json_pretty(msg))
80
+ end
81
+ end
82
+
83
+ # Why lock the error file?
84
+ #
85
+ # Well because ec-backup has a concurrency options that
86
+ # will allow you to backup and restore things in parallel,
87
+ # therefor we need to ensure that only one process is
88
+ # writing to the error file.
89
+ def lock_file(file_name, mode)
90
+ File.open(file_name, mode) do |f|
91
+ begin
92
+ f.flock ::File::LOCK_EX
93
+ yield f
94
+ ensure
95
+ f.flock ::File::LOCK_UN
96
+ end
97
+ end
98
+ end
99
+
100
+ def display(file_name = @err_file)
101
+ # Print summary report only if error file exist
102
+ return unless File.exist?(file_name)
103
+
104
+ puts "\nError Summary Report"
105
+ lock_file(file_name, 'r') do |f|
106
+ f.each_line do |line|
107
+ puts line
108
+ end
109
+ end
110
+ puts "\nError(s) Summary file located at: '#{file_name}'"
111
+ end
112
+ end
113
+ end
114
+ end
@@ -26,7 +26,7 @@ class Chef
26
26
  require 'chef/chef_fs/file_pattern'
27
27
  # Work around bug in chef_fs
28
28
  require 'chef/chef_fs/command_line'
29
- require 'chef/chef_fs/file_system/acl_entry'
29
+ require 'chef/chef_fs/file_system/chef_server/acl_entry'
30
30
  require 'chef/chef_fs/data_handler/acl_data_handler'
31
31
  require 'securerandom'
32
32
  require 'chef/chef_fs/parallelizer'
@@ -63,11 +63,11 @@ class Chef
63
63
  def create_organization(orgname)
64
64
  org = JSONCompat.from_json(File.read("#{dest_dir}/organizations/#{orgname}/org.json"))
65
65
  rest.post('organizations', org)
66
- rescue Net::HTTPServerException => e
67
- if e.response.code == "409"
66
+ rescue Net::HTTPServerException => ex
67
+ if ex.response.code == "409"
68
68
  rest.put("organizations/#{orgname}", org)
69
69
  else
70
- raise
70
+ knife_ec_error_handler.add(ex)
71
71
  end
72
72
  end
73
73
 
@@ -76,9 +76,10 @@ class Chef
76
76
  invitations.each do |invitation|
77
77
  begin
78
78
  rest.post("organizations/#{orgname}/association_requests", { 'user' => invitation['username'] })
79
- rescue Net::HTTPServerException => e
80
- if e.response.code != "409"
79
+ rescue Net::HTTPServerException => ex
80
+ if ex.response.code != "409"
81
81
  ui.error("Cannot create invitation #{invitation['id']}")
82
+ knife_ec_error_handler.add(ex)
82
83
  end
83
84
  end
84
85
  end
@@ -92,10 +93,8 @@ class Chef
92
93
  response = rest.post("organizations/#{orgname}/association_requests", { 'user' => username })
93
94
  association_id = response["uri"].split("/").last
94
95
  rest.put("users/#{username}/association_requests/#{association_id}", { 'response' => 'accept' })
95
- rescue Net::HTTPServerException => e
96
- if e.response.code != "409"
97
- raise
98
- end
96
+ rescue Net::HTTPServerException => ex
97
+ knife_ec_error_handler.add(ex) if ex.response.code != "409"
99
98
  end
100
99
  end
101
100
  end
@@ -137,12 +136,12 @@ class Chef
137
136
  user_with_password = user.dup
138
137
  user_with_password['password'] = SecureRandom.hex
139
138
  rest.post('users', user_with_password)
140
- rescue Net::HTTPServerException => e
141
- if e.response.code == "409"
139
+ rescue Net::HTTPServerException => ex
140
+ if ex.response.code == "409"
142
141
  rest.put("users/#{name}", user)
143
- else
144
- raise
142
+ next
145
143
  end
144
+ knife_ec_error_handler.add(ex)
146
145
  end
147
146
  end
148
147
  purge_users_on_restore
@@ -245,10 +244,7 @@ class Chef
245
244
  end
246
245
 
247
246
  ['/acls/groups/billing-admins.json', '/acls/groups/public_key_read_access.json'].each do |acl|
248
- pattern = Chef::ChefFS::FilePattern.new(acl)
249
- Chef::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.local_fs,
250
- chef_fs_config.chef_fs, nil, config, ui,
251
- proc { |entry| chef_fs_config.format_path(entry)})
247
+ chef_fs_copy_pattern(acl, chef_fs_config)
252
248
  end
253
249
 
254
250
  Chef::Config.node_name = if config[:skip_version]
@@ -276,7 +272,7 @@ class Chef
276
272
  # - groups must be uploaded twice to account for Chef Server versions that don't
277
273
  # accept group members on POST
278
274
  (top_level_paths + group_paths*2 + group_acl_paths + acl_paths).each do |path|
279
- Chef::ChefFS::FileSystem.copy_to(Chef::ChefFS::FilePattern.new(path), chef_fs_config.local_fs, chef_fs_config.chef_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
275
+ chef_fs_copy_pattern(path, chef_fs_config)
280
276
  end
281
277
 
282
278
  # restore clients to groups, using the pivotal user again
@@ -289,6 +285,23 @@ class Chef
289
285
  end
290
286
  end
291
287
 
288
+ # ChefFS copy pattern inside the EcRestore class will
289
+ # copy from the local_fs to the Chef Server.
290
+ #
291
+ # NOTE: Do not get confused, this is the other way around
292
+ # from how we implemented in EcBackup. Therefor we can't
293
+ # abstract it inside EcBase.
294
+ def chef_fs_copy_pattern(pattern_str, chef_fs_config)
295
+ ui.msg "Copying #{pattern_str}"
296
+ pattern = Chef::ChefFS::FilePattern.new(pattern_str)
297
+ Chef::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.local_fs,
298
+ chef_fs_config.chef_fs, nil,
299
+ config, ui,
300
+ proc { |entry| chef_fs_config.format_path(entry) })
301
+ rescue Net::HTTPServerException => ex
302
+ knife_ec_error_handler.add(ex)
303
+ end
304
+
292
305
  # Takes an array of group objects
293
306
  # and topologically sorts them
294
307
  def sort_groups_for_upload(groups)
@@ -347,6 +360,8 @@ class Chef
347
360
  rest.put("#{url}/#{permission}", { permission => acls[permission] })
348
361
  end
349
362
  end
363
+ rescue Net::HTTPServerException => ex
364
+ knife_ec_error_handler.add(ex)
350
365
  end
351
366
  end
352
367
  end
@@ -1,5 +1,6 @@
1
1
  require 'uri'
2
2
  require 'openssl'
3
+ require 'chef/server_api'
3
4
 
4
5
  class Chef
5
6
  class Server
@@ -29,7 +30,7 @@ class Chef
29
30
  end
30
31
 
31
32
  def direct_account_access?
32
- Chef::REST.new("http://127.0.0.1:9465").get("users")
33
+ Chef::ServerAPI.new("http://127.0.0.1:9465").get("users")
33
34
  true
34
35
  rescue
35
36
  false
@@ -1,3 +1,3 @@
1
1
  module KnifeECBackup
2
- VERSION = '2.1.0'
2
+ VERSION = '2.2.0'
3
3
  end
@@ -3,6 +3,7 @@ require 'chef/knife/ec_backup'
3
3
  require 'fakefs/spec_helpers'
4
4
 
5
5
  describe Chef::Knife::EcBackup do
6
+ let(:dest_dir) { Dir.mktmpdir }
6
7
  USER_RESPONSE = {
7
8
  "foo" => "organizations/bar/users/foo",
8
9
  "bar" => "organizations/bar/users/bar"
@@ -30,9 +31,10 @@ describe Chef::Knife::EcBackup do
30
31
  before(:each) do
31
32
  Chef::Knife::EcBackup.load_deps
32
33
  @knife = Chef::Knife::EcBackup.new
33
- @rest = double('Chef::Rest')
34
+ @rest = double('Chef::ServerAPI')
34
35
  allow(@knife).to receive(:rest).and_return(@rest)
35
36
  allow(@knife).to receive(:user_acl_rest).and_return(@rest)
37
+ allow_any_instance_of(Chef::Knife::EcBase).to receive(:dest_dir).and_return(dest_dir)
36
38
  end
37
39
 
38
40
  describe "#for_each_user" do
@@ -40,6 +42,18 @@ describe Chef::Knife::EcBackup do
40
42
  allow(@rest).to receive(:get).with("/users").and_return(USER_RESPONSE)
41
43
  expect{ |b| @knife.for_each_user(&b) }.to yield_successive_args(["foo", USER_RESPONSE["foo"]], ["bar", USER_RESPONSE["bar"]])
42
44
  end
45
+
46
+ context "when there are HTTP failures" do
47
+ let(:ec_error_handler) { double("Chef::Knife::EcErrorHandler") }
48
+
49
+ it "adds exceptions to error handler" do
50
+ exception = net_exception(500)
51
+ allow(Chef::Knife::EcErrorHandler).to receive(:new).and_return(ec_error_handler)
52
+ allow(@rest).to receive(:get).with("/users").and_raise(exception)
53
+ expect(ec_error_handler).to receive(:add).at_least(1).with(exception)
54
+ @knife.for_each_user
55
+ end
56
+ end
43
57
  end
44
58
 
45
59
  describe "#for_each_organization" do
@@ -71,12 +85,39 @@ describe Chef::Knife::EcBackup do
71
85
  expect{ |b| @knife.for_each_organization(&b) }.to yield_successive_args(org_response("bar"),
72
86
  org_response("foo", true))
73
87
  end
88
+
89
+ context "when there are HTTP failures" do
90
+ let(:ec_error_handler) { double("Chef::Knife::EcErrorHandler") }
91
+
92
+ before(:each) do
93
+ server = double('Chef::Server')
94
+ allow(Chef::Server).to receive(:new).and_return(server)
95
+ allow(Chef::Knife::EcErrorHandler).to receive(:new).and_return(ec_error_handler)
96
+ allow(server).to receive(:version).and_return(Gem::Version.new("12.0.0"))
97
+ end
98
+
99
+ it "adds exception and continues with the rest of the orgs" do
100
+ exception = net_exception(404)
101
+ allow(@rest).to receive(:get).with("organizations/foo").and_return(org_response("foo"))
102
+ allow(@rest).to receive(:get).with("organizations/bar").and_raise(exception)
103
+ expect(ec_error_handler).to receive(:add).at_least(1).with(exception)
104
+ expect{ |b| @knife.for_each_organization(&b) }.to yield_successive_args(org_response("foo"))
105
+ end
106
+
107
+ it "adds exceptions to error handler" do
108
+ allow(@rest).to receive(:get).with("organizations/foo").and_raise(net_exception(500))
109
+ allow(@rest).to receive(:get).with("organizations/bar").and_raise(net_exception(404))
110
+ expect(ec_error_handler).to receive(:add).at_least(2)
111
+ @knife.for_each_organization
112
+ end
113
+ end
74
114
  end
75
115
 
76
116
  describe "#download_user" do
77
117
  include FakeFS::SpecHelpers
78
118
  let (:username) { "foo" }
79
119
  let (:url) { "users/foo" }
120
+ before(:each) { FileUtils.mkdir_p(File.join(dest_dir, "users")) }
80
121
 
81
122
  it "downloads a named user from the api" do
82
123
  expect(@rest).to receive(:get).with(url)
@@ -87,13 +128,14 @@ describe Chef::Knife::EcBackup do
87
128
  user_response = {"username" => "foo"}
88
129
  allow(@rest).to receive(:get).with(url).and_return(user_response)
89
130
  @knife.download_user(username, url)
90
- expect(JSON.parse(File.read("/users/foo.json"))).to eq(user_response)
131
+ expect(JSON.parse(File.read("#{dest_dir}/users/foo.json"))).to eq(user_response)
91
132
  end
92
133
  end
93
134
 
94
135
  describe "#download_user_acl" do
95
136
  include FakeFS::SpecHelpers
96
137
  let (:username) {"foo"}
138
+ before(:each) { FileUtils.mkdir_p(File.join(dest_dir, "user_acls")) }
97
139
 
98
140
  it "downloads a user acl from the API" do
99
141
  expect(@rest).to receive(:get).with("users/#{username}/_acl")
@@ -104,7 +146,19 @@ describe Chef::Knife::EcBackup do
104
146
  user_acl_response = {"create" => {}}
105
147
  allow(@rest).to receive(:get).with("users/#{username}/_acl").and_return(user_acl_response)
106
148
  @knife.download_user_acl(username)
107
- expect(JSON.parse(File.read("/user_acls/foo.json"))).to eq(user_acl_response)
149
+ expect(JSON.parse(File.read("#{dest_dir}/user_acls/foo.json"))).to eq(user_acl_response)
150
+ end
151
+
152
+ context "when there are HTTP failures" do
153
+ let(:ec_error_handler) { double("Chef::Knife::EcErrorHandler") }
154
+
155
+ it "adds exceptions to error handler" do
156
+ exception = net_exception(500)
157
+ allow(Chef::Knife::EcErrorHandler).to receive(:new).and_return(ec_error_handler)
158
+ allow(@rest).to receive(:get).with("users/#{username}/_acl").and_raise(exception)
159
+ expect(ec_error_handler).to receive(:add).at_least(1).with(exception)
160
+ @knife.download_user_acl(username)
161
+ end
108
162
  end
109
163
  end
110
164
 
@@ -113,7 +167,7 @@ describe Chef::Knife::EcBackup do
113
167
  it "writes the given object to disk" do
114
168
  org_object = { "name" => "bob" }
115
169
  @knife.write_org_object_to_disk(org_object)
116
- expect(JSON.parse(File.read("/organizations/bob/org.json"))).to eq(org_object)
170
+ expect(JSON.parse(File.read("#{dest_dir}/organizations/bob/org.json"))).to eq(org_object)
117
171
  end
118
172
  end
119
173
 
@@ -137,8 +191,21 @@ describe Chef::Knife::EcBackup do
137
191
  it "writes the org members to a JSON file" do
138
192
  expect(@rest).to receive(:get).with("/organizations/bob/users").and_return(users)
139
193
  @knife.download_org_members("bob")
140
- expect(JSON.parse(File.read("/organizations/bob/members.json"))).to eq(users)
194
+ expect(JSON.parse(File.read("#{dest_dir}/organizations/bob/members.json"))).to eq(users)
141
195
  end
196
+
197
+ context "when there are HTTP failures" do
198
+ let(:ec_error_handler) { double("Chef::Knife::EcErrorHandler") }
199
+
200
+ it "adds exceptions to error handler" do
201
+ exception = net_exception(500)
202
+ allow(Chef::Knife::EcErrorHandler).to receive(:new).and_return(ec_error_handler)
203
+ allow(@rest).to receive(:get).with("/organizations/bob/users").and_raise(exception)
204
+ expect(ec_error_handler).to receive(:add).at_least(1).with(exception)
205
+ @knife.download_org_members("bob")
206
+ end
207
+ end
208
+
142
209
  end
143
210
 
144
211
  describe "#download_org_inivitations" do
@@ -152,7 +219,19 @@ describe Chef::Knife::EcBackup do
152
219
  it "writes the invitations to a JSON file" do
153
220
  expect(@rest).to receive(:get).with("/organizations/bob/association_requests").and_return(invites)
154
221
  @knife.download_org_invitations("bob")
155
- expect(JSON.parse(File.read("/organizations/bob/invitations.json"))).to eq(invites)
222
+ expect(JSON.parse(File.read("#{dest_dir}/organizations/bob/invitations.json"))).to eq(invites)
223
+ end
224
+
225
+ context "when there are HTTP failures" do
226
+ let(:ec_error_handler) { double("Chef::Knife::EcErrorHandler") }
227
+
228
+ it "adds exceptions to error handler" do
229
+ exception = net_exception(500)
230
+ allow(Chef::Knife::EcErrorHandler).to receive(:new).and_return(ec_error_handler)
231
+ allow(@rest).to receive(:get).with("/organizations/bob/association_requests").and_raise(exception)
232
+ expect(ec_error_handler).to receive(:add).at_least(1).with(exception)
233
+ @knife.download_org_invitations("bob")
234
+ end
156
235
  end
157
236
  end
158
237
  end
@@ -0,0 +1,88 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
2
+ require 'chef/knife/ec_error_handler'
3
+ require 'chef/knife/ec_backup'
4
+ require 'chef/knife/ec_restore'
5
+ require 'fakefs/spec_helpers'
6
+
7
+ def net_exception(code)
8
+ s = double("status", :code => code.to_s)
9
+ Net::HTTPServerException.new("I'm not real!", s)
10
+ end
11
+
12
+ describe Chef::Knife::EcErrorHandler do
13
+ let(:dest_dir) { Dir.mktmpdir }
14
+ let(:err_dir) { File.join(dest_dir, "errors") }
15
+
16
+ before(:each) do
17
+ allow(Time).to receive(:now).and_return(Time.new(1988, 04, 17, 0, 0, 0, "+00:00")) #=> 1988-04-17 00:00:00 +0000
18
+ @knife_ec_error_handler = described_class.new(dest_dir, Class)
19
+ end
20
+
21
+ describe "#initialize" do
22
+ it "creates an error directory" do
23
+ expect(FileUtils).to receive(:mkdir_p).with("#{dest_dir}/errors")
24
+ described_class.new(dest_dir, Class)
25
+ end
26
+
27
+ it "sets up an err_file depending of the class that comes from" do
28
+ ec_backup = described_class.new(dest_dir, Chef::Knife::EcBackup)
29
+ expect(ec_backup.err_file).to match File.join(err_dir, "backup-1988-04-17T00:00:00+00:00.json")
30
+ ec_restore = described_class.new(dest_dir, Chef::Knife::EcRestore)
31
+ expect(ec_restore.err_file).to match File.join(err_dir, "restore-1988-04-17T00:00:00+00:00.json")
32
+ ec_other = described_class.new(dest_dir, Class)
33
+ expect(ec_other.err_file).to match File.join(err_dir, "other-1988-04-17T00:00:00+00:00.json")
34
+ end
35
+ end
36
+
37
+ it "#display" do
38
+ @knife_ec_error_handler.add(net_exception(123))
39
+ expect { @knife_ec_error_handler.display }.to output(/
40
+ Error Summary Report
41
+ {
42
+ "timestamp": "1988-04-17 00:00:00 \+0000",
43
+ "message": "I'm not real!",
44
+ "backtrace": null,
45
+ "exception": "Net::HTTPServerException"
46
+ }
47
+ /).to_stdout
48
+ end
49
+
50
+ it "#add" do
51
+ mock_content = <<-EOF
52
+ {
53
+ "timestamp": "1988-04-17 00:00:00 +0000",
54
+ "message": "I'm not real!",
55
+ "backtrace": null,
56
+ "exception": "Net::HTTPServerException"
57
+ }{
58
+ "timestamp": "1988-04-17 00:00:00 +0000",
59
+ "message": "I'm not real!",
60
+ "backtrace": null,
61
+ "exception": "Net::HTTPServerException"
62
+ }{
63
+ "timestamp": "1988-04-17 00:00:00 +0000",
64
+ "message": "I'm not real!",
65
+ "backtrace": null,
66
+ "exception": "Net::HTTPServerException"
67
+ }{
68
+ "timestamp": "1988-04-17 00:00:00 +0000",
69
+ "message": "I'm not real!",
70
+ "backtrace": null,
71
+ "exception": "Net::HTTPServerException"
72
+ }
73
+ EOF
74
+ err_file = @knife_ec_error_handler.err_file
75
+ expect(Chef::JSONCompat).to receive(:to_json_pretty)
76
+ .at_least(4)
77
+ .and_call_original
78
+ expect(File).to receive(:open)
79
+ .at_least(4)
80
+ .with(err_file, "a")
81
+ .and_call_original
82
+ @knife_ec_error_handler.add(net_exception(500))
83
+ @knife_ec_error_handler.add(net_exception(409))
84
+ @knife_ec_error_handler.add(net_exception(404))
85
+ @knife_ec_error_handler.add(net_exception(123))
86
+ expect(File.read(err_file)).to match mock_content.strip
87
+ end
88
+ end
@@ -9,25 +9,26 @@ describe Chef::Knife::EcKeyBase do
9
9
  let (:knife) { Chef::Knife::KeyBaseTester.new }
10
10
 
11
11
  let(:running_server_postgresql_sql_config_json) {
12
- '{"private_chef": { "opscode-erchef":{}, "postgresql": { "sql_user": "jiminy", "sql_password": "secret"} }}'
12
+ '{"private_chef": { "opscode-erchef":{}, "postgresql": { "sql_user": "jiminy", "sql_password": "secret"} }, "postgresql": { "sql_user": "jiminy", "sql_password": "secret"} }'
13
13
  }
14
14
 
15
15
 
16
16
  let(:running_server_erchef_config_json) {
17
- '{"private_chef": { "opscode-erchef": { "sql_user": "cricket", "sql_password": "secrete"}, "postgresql" : {} }}'
17
+ '{"private_chef": { "opscode-erchef": { "sql_user": "cricket", "sql_password": "secrete"}}, "opscode_erchef": { "sql_user": "cricket", "sql_password": "secrete"}}'
18
18
  }
19
19
  describe "#load_config_from_file!" do
20
20
  before(:each) do
21
21
  allow(File).to receive(:exists?).and_return(true)
22
+ allow(File).to receive(:size).and_return(1)
22
23
  end
23
24
  it "correctly sets sql options when they live under postgresql settings" do
24
- allow(File).to receive(:read).and_return(running_server_postgresql_sql_config_json)
25
+ allow(IO).to receive(:read).and_return(running_server_postgresql_sql_config_json)
25
26
  knife.load_config_from_file!
26
27
  expect(knife.config[:sql_user]).to eq("jiminy")
27
28
  expect(knife.config[:sql_password]).to eq("secret")
28
29
  end
29
30
  it "correctly sets sql options when they live under opscode-erchef settings" do
30
- allow(File).to receive(:read).and_return(running_server_erchef_config_json)
31
+ allow(IO).to receive(:read).and_return(running_server_erchef_config_json)
31
32
  knife.load_config_from_file!
32
33
  expect(knife.config[:sql_user]).to eq("cricket")
33
34
  expect(knife.config[:sql_password]).to eq("secrete")
@@ -23,7 +23,7 @@ describe Chef::Knife::EcRestore do
23
23
  before(:each) do
24
24
  Chef::Knife::EcRestore.load_deps
25
25
  @knife = Chef::Knife::EcRestore.new
26
- @rest = double('Chef::Rest')
26
+ @rest = double('Chef::ServerAPI')
27
27
  allow(@knife).to receive(:rest).and_return(@rest)
28
28
  allow(@knife).to receive(:user_acl_rest).and_return(@rest)
29
29
  end
@@ -134,6 +134,19 @@ describe Chef::Knife::EcRestore do
134
134
  expect(@rest).to receive(:put).with("users/jane", {"username" => "jane"})
135
135
  @knife.restore_users
136
136
  end
137
+
138
+ context "when there are HTTP failures with different code than 409" do
139
+ let(:ec_error_handler) { double("Chef::Knife::EcErrorHandler") }
140
+
141
+ it "adds exceptions to error handler" do
142
+ make_user "jane"
143
+ exception = net_exception(500)
144
+ allow(Chef::Knife::EcErrorHandler).to receive(:new).and_return(ec_error_handler)
145
+ allow(@rest).to receive(:post).with("users", anything).and_raise(exception)
146
+ expect(ec_error_handler).to receive(:add).at_least(1).with(exception)
147
+ @knife.restore_users
148
+ end
149
+ end
137
150
  end
138
151
 
139
152
  describe "#restore_group" do
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
2
  require 'chef/server'
3
- require 'chef/rest'
3
+ require 'chef/server_api'
4
4
  require 'stringio'
5
5
 
6
6
  describe Chef::Server do
@@ -35,7 +35,7 @@ describe Chef::Server do
35
35
  it "knows when account is directly accessible" do
36
36
  s = Chef::Server.new("http://api.example.com")
37
37
  rest = double('rest')
38
- allow(Chef::REST).to receive(:new).and_return(rest)
38
+ allow(Chef::ServerAPI).to receive(:new).and_return(rest)
39
39
  allow(rest).to receive(:get).and_return("")
40
40
  expect(s.direct_account_access?).to eq(true)
41
41
  end
@@ -43,7 +43,7 @@ describe Chef::Server do
43
43
  it "knows when account is not directly accessible" do
44
44
  s = Chef::Server.new("http://api.example.com")
45
45
  rest = double('rest')
46
- allow(Chef::REST).to receive(:new).and_return(rest)
46
+ allow(Chef::ServerAPI).to receive(:new).and_return(rest)
47
47
  allow(rest).to receive(:get).and_raise(Errno::ECONNREFUSED)
48
48
  expect(s.direct_account_access?).to eq(false)
49
49
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-ec-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-25 00:00:00.000000000 Z
11
+ date: 2017-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -79,6 +79,7 @@ files:
79
79
  - Rakefile
80
80
  - lib/chef/knife/ec_backup.rb
81
81
  - lib/chef/knife/ec_base.rb
82
+ - lib/chef/knife/ec_error_handler.rb
82
83
  - lib/chef/knife/ec_key_base.rb
83
84
  - lib/chef/knife/ec_key_export.rb
84
85
  - lib/chef/knife/ec_key_import.rb
@@ -89,6 +90,7 @@ files:
89
90
  - lib/knife_ec_backup/version.rb
90
91
  - spec/chef/knife/ec_backup_spec.rb
91
92
  - spec/chef/knife/ec_base_spec.rb
93
+ - spec/chef/knife/ec_error_handler_spec.rb
92
94
  - spec/chef/knife/ec_key_base_spec.rb
93
95
  - spec/chef/knife/ec_key_export_spec.rb
94
96
  - spec/chef/knife/ec_key_import_spec.rb
@@ -116,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
118
  version: '0'
117
119
  requirements: []
118
120
  rubyforge_project:
119
- rubygems_version: 2.6.8
121
+ rubygems_version: 2.6.11
120
122
  signing_key:
121
123
  specification_version: 4
122
124
  summary: Backup and Restore of Enterprise Chef