knife-ec-backup 2.1.0 → 2.2.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.
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