knife-ec-backup 2.0.7 → 2.1.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: b97714005f59fe60b36667b628c58f9de216f1e2
4
- data.tar.gz: 8ba4fc5291580630844c140dea952c78d4a69db2
3
+ metadata.gz: 5c0eaeaca5c1a8707e66a26cafe08186a77ea6fa
4
+ data.tar.gz: 2b4675644bc5a526f3a26ad436dfd73a30ed1307
5
5
  SHA512:
6
- metadata.gz: 49ea3afa7fa7ed54a318c828b235703fda5b38d28a171124ee4837efc43867ffecb4ff46b157c82aee9c7d93da65d39041f6aeb2f50f5792903e22d536e82f2c
7
- data.tar.gz: 3443451e72baa28454cf805cfc8c20a241cc21bcf14ff286beb3d80e5caab8bc33d6c658f3191562d2ee0a59c5b8f482835be1b3028d15b272d7e2d433048af7
6
+ metadata.gz: ba31ebcf31a0aca8b4df975c9687436822c5fabdc81095b23e30c4e60e35f7ff88516334949ad53fa86c4d8fe4c6787c1ed5764fa9ddd0057e132ee73f78eab1
7
+ data.tar.gz: e751b01f0094618a3fb5a6249ef083a42d20e911135d69d07b9bba77a4438b4118fb1f7f80201ec86ec7cd392865f1d8241775c8b4f9e83305ad9fbf3d6871e8
data/README.md CHANGED
@@ -84,23 +84,29 @@ It is recommended that you run this from a frontend Enterprise Chef Server, you
84
84
 
85
85
  # Subcommands
86
86
 
87
- ## Common Option
87
+ ## Common Options
88
88
 
89
89
  The following options are supported across all subcommands:
90
90
 
91
- * `--sql_host`:
91
+ * `--sql-host`:
92
92
  The hostname of the Chef Server's postgresql server. (default: localhost)
93
93
 
94
- * `--sql_port`:
94
+ * `--sql-port`:
95
95
  The postgresql listening port on the Chef Server. (default: 5432)
96
96
 
97
- * `--sql_user`:
97
+ * `--sql-user`:
98
98
  The username of postgresql user with access to the opscode_chef
99
99
  database. (default: autoconfigured from
100
100
  /etc/opscode/chef-server-running.json)
101
101
 
102
- * `--sql_password`:
103
- The password for the sql_user. (default: autoconfigured from /etc/opscode/chef-server-running.json)
102
+ * `--sql-password`:
103
+ The password for the sql-user. (default: autoconfigured from /etc/opscode/chef-server-running.json)
104
+
105
+ * `--purge`:
106
+ Whether to sync deletions from backup source to restore destination. (default: false)
107
+
108
+ * `--dry-run`:
109
+ Report what actions would be taken without performing any. (default: false)
104
110
 
105
111
  ## knife ec backup DEST_DIR (options)
106
112
 
@@ -24,6 +24,9 @@ class Chef
24
24
  set_skip_user_acl!
25
25
  ensure_webui_key_exists!
26
26
 
27
+ ensure_dir("#{dest_dir}/users")
28
+ ensure_dir("#{dest_dir}/user_acls") unless config[:skip_useracl]
29
+ ui.msg 'Downloading Users'
27
30
  for_each_user do |username, url|
28
31
  download_user(username, url)
29
32
  if config[:skip_useracl]
@@ -32,6 +35,7 @@ class Chef
32
35
  download_user_acl(username)
33
36
  end
34
37
  end
38
+ purge_users_on_backup
35
39
 
36
40
  if config[:with_user_sql] || config[:with_key_sql]
37
41
  export_from_sql
@@ -47,8 +51,30 @@ class Chef
47
51
  end
48
52
  end
49
53
 
54
+ def users_for_purge
55
+ purge_list = local_user_list - remote_user_list
56
+ # failsafe - don't delete pivotal
57
+ purge_list -= ['pivotal']
58
+ purge_list.each do |user|
59
+ yield user
60
+ end
61
+ end
62
+
63
+ def purge_users_on_backup
64
+ return unless config[:purge]
65
+ users_for_purge do |user|
66
+ ui.msg "Deleting user #{user} from local backup (purge is on)"
67
+ begin
68
+ ::File.delete("#{dest_dir}/users/#{user}.json")
69
+ ::File.delete("#{dest_dir}/user_acls/#{user}.json")
70
+ rescue Errno::ENOENT => e
71
+ ui.warn "Failed to find local #{user} data #{e}"
72
+ end
73
+ end
74
+ end
75
+
50
76
  def for_each_user
51
- rest.get('/users').each_pair do |name, url|
77
+ remote_users.each_pair do |name, url|
52
78
  yield name, url
53
79
  end
54
80
  end
@@ -73,14 +99,12 @@ class Chef
73
99
  end
74
100
 
75
101
  def download_user(username, url)
76
- ensure_dir("#{dest_dir}/users")
77
102
  File.open("#{dest_dir}/users/#{username}.json", 'w') do |file|
78
103
  file.write(Chef::JSONCompat.to_json_pretty(rest.get(url)))
79
104
  end
80
105
  end
81
106
 
82
107
  def download_user_acl(username)
83
- ensure_dir("#{dest_dir}/user_acls")
84
108
  File.open("#{dest_dir}/user_acls/#{username}.json", 'w') do |file|
85
109
  file.write(Chef::JSONCompat.to_json_pretty(user_acl_rest.get("users/#{username}/_acl")))
86
110
  end
@@ -152,16 +176,30 @@ class Chef
152
176
  chef_fs_copy_pattern('/groups/public_key_read_access.json', chef_fs_config)
153
177
  chef_fs_copy_pattern('/groups/admins.json', chef_fs_config)
154
178
 
155
- # Set Chef::Config to use an organization administrator
156
- Chef::Config.node_name = org_admin
179
+ Chef::Config.node_name = if config[:skip_version]
180
+ org_admin
181
+ else
182
+ server.supports_defaulting_to_pivotal? ? 'pivotal' : org_admin
183
+ end
157
184
 
158
- # Download the entire org skipping the billing-admins, public_key_read_access group ACLs and the groups themselves
159
185
  chef_fs_config = Chef::ChefFS::Config.new
160
186
  top_level_paths = chef_fs_config.chef_fs.children.select { |entry| entry.name != 'acls' && entry.name != 'groups' }.map { |entry| entry.path }
161
- acl_paths = chef_fs_paths('/acls/*', chef_fs_config, ['groups'])
162
- group_acl_paths = chef_fs_paths('/acls/groups/*', chef_fs_config, ['billing-admins','public_key_read_access'])
163
- group_paths = chef_fs_paths('/groups/*', chef_fs_config, ['billing-admins','public_key_read_access'])
164
- (top_level_paths + group_acl_paths + acl_paths + group_paths).each do |path|
187
+
188
+ # The top level acl object names end with .json extension
189
+ # Therefore we can use Chef::ChefFS::FilePattern matching for items
190
+ # such as /acls/organizations.json
191
+ #
192
+ # 2nd level leaf /acl/*/* objects as well as /groups/* objects do not end with .json
193
+ # therefore we use normalize_path_name to add the .json extension
194
+ # for example: /acls/environments/_default
195
+
196
+ # Skip the billing-admins, public_key_read_access group ACLs and the groups since they've already been copied
197
+ exclude_list = ['billing-admins','public_key_read_access']
198
+
199
+ top_level_acls = chef_fs_paths('/acls/*.json', chef_fs_config, [])
200
+ acl_paths = chef_fs_paths('/acls/*/*', chef_fs_config, exclude_list)
201
+ group_paths = chef_fs_paths('/groups/*', chef_fs_config, exclude_list)
202
+ (top_level_paths + top_level_acls + acl_paths + group_paths).each do |path|
165
203
  chef_fs_copy_pattern(path, chef_fs_config)
166
204
  end
167
205
  ensure
@@ -18,11 +18,13 @@
18
18
 
19
19
  require 'chef/knife'
20
20
  require 'chef/server_api'
21
+ require 'veil'
21
22
 
22
23
  class Chef
23
24
  class Knife
24
25
  module EcBase
25
26
  class NoAdminFound < Exception; end
27
+ class UnImplemented < Exception; end
26
28
 
27
29
  def self.included(includer)
28
30
  includer.class_eval do
@@ -33,8 +35,12 @@ class Chef
33
35
 
34
36
  option :webui_key,
35
37
  :long => '--webui-key KEYPATH',
36
- :description => 'Path to the WebUI Key (default: /etc/opscode/webui_priv.pem)',
37
- :default => '/etc/opscode/webui_priv.pem'
38
+ :description => 'Path to the WebUI Key (default: Read from secrets store or /etc/opscode/webui_priv.pem)'
39
+
40
+ option :secrets_file_path,
41
+ :long => '--secrets-file PATH',
42
+ :description => 'Path to a valid private-chef-secrets.json file (default: /etc/opscode/private-chef-secrets.json)',
43
+ :default => '/etc/opscode/private-chef-secrets.json'
38
44
 
39
45
  option :skip_useracl,
40
46
  :long => '--skip-useracl',
@@ -78,6 +84,17 @@ class Chef
78
84
  :long => '--with-key-sql',
79
85
  :description => 'Try direct data base access for key table export/import. Required to properly handle rotated keys.'
80
86
 
87
+ option :purge,
88
+ :long => '--purge',
89
+ :boolean => true | false,
90
+ :default => false,
91
+ :description => 'Syncs deletions from backup source to restore destination.'
92
+
93
+ option :dry_run,
94
+ :long => '--dry-run',
95
+ :boolean => true | false,
96
+ :default => false,
97
+ :description => 'Report what actions would be taken without performing any.'
81
98
  end
82
99
 
83
100
  attr_accessor :dest_dir
@@ -116,6 +133,23 @@ class Chef
116
133
  @rest ||= Chef::ServerAPI.new(server.root_url, {:api_version => "0"})
117
134
  end
118
135
 
136
+ def remote_users
137
+ @remote_users ||= rest.get('/users')
138
+ end
139
+
140
+ def remote_user_list
141
+ @remote_user_list ||= remote_users.keys
142
+ end
143
+
144
+ def local_user_list
145
+ @local_user_list ||= Dir.glob("#{dest_dir}/users/*\.json").map { |u| File.basename(u, '.json') }
146
+ end
147
+
148
+ def users_for_purge
149
+ # not itended to be called from ec_base
150
+ raise Chef::Knife::EcBase::UnImplemented
151
+ end
152
+
119
153
  def user_acl_rest
120
154
  @user_acl_rest ||= if config[:skip_version]
121
155
  rest
@@ -134,7 +168,7 @@ class Chef
134
168
  def set_client_config!
135
169
  Chef::Config.custom_http_headers = (Chef::Config.custom_http_headers || {}).merge({'x-ops-request-source' => 'web'})
136
170
  Chef::Config.node_name = 'pivotal'
137
- Chef::Config.client_key = config[:webui_key]
171
+ Chef::Config.client_key = webui_key
138
172
  end
139
173
 
140
174
  def set_dest_dir_from_args!
@@ -145,8 +179,39 @@ class Chef
145
179
  @dest_dir = name_args[0]
146
180
  end
147
181
 
182
+ def webui_key
183
+ if config[:webui_key]
184
+ config[:webui_key]
185
+ elsif veil.exist?("chef-server", "webui_key")
186
+ temporary_webui_key
187
+ else
188
+ '/etc/opscode/webui_priv.pem'
189
+ end
190
+ end
191
+
192
+ def veil_config
193
+ { provider: 'chef-secrets-file',
194
+ path: config[:secrets_file_path] }
195
+ end
196
+
197
+ def veil
198
+ Veil::CredentialCollection.from_config(veil_config)
199
+ end
200
+
201
+ def temporary_webui_key
202
+ @temp_webui_key ||= begin
203
+ key_data = veil.get("chef-server", "webui_key")
204
+ f = Tempfile.new("knife-ec-backup")
205
+ f.write(key_data)
206
+ f.flush
207
+ f.close
208
+ f
209
+ end
210
+ @temp_webui_key.path
211
+ end
212
+
148
213
  def ensure_webui_key_exists!
149
- if !File.exist?(config[:webui_key])
214
+ if !File.exist?(webui_key)
150
215
  ui.error("Webui Key (#{config[:webui_key]}) does not exist.")
151
216
  exit 1
152
217
  end
@@ -48,6 +48,11 @@ class Chef
48
48
  :long => "--sql-password PASSWORD",
49
49
  :description => 'Password used to connect to the postgresql database'
50
50
 
51
+ option :secrets_file_path,
52
+ :long => '--secrets-file PATH',
53
+ :description => 'Path to a valid private-chef-secrets.json file (default: /etc/opscode/private-chef-secrets.json)',
54
+ :default => '/etc/opscode/private-chef-secrets.json'
55
+
51
56
  option :skip_keys_table,
52
57
  :long => "--skip-keys-table",
53
58
  :description => "Skip Chef 12-only keys table",
@@ -83,7 +88,26 @@ class Chef
83
88
  'postgresql'
84
89
  end
85
90
  config[:sql_user] ||= running_config['private_chef'][hash_key]['sql_user']
86
- config[:sql_password] ||= running_config['private_chef'][hash_key]['sql_password']
91
+ config[:sql_password] ||= sql_password
92
+ end
93
+ end
94
+
95
+ def veil_config
96
+ { provider: 'chef-secrets-file',
97
+ path: config[:secrets_file_path] }
98
+ end
99
+
100
+ def veil
101
+ Veil::CredentialCollection.from_config(veil_config)
102
+ end
103
+
104
+ def sql_password
105
+ if config[:sql_password]
106
+ config[:sql_password]
107
+ elsif veil.exist?("opscode_erchef", "sql_password")
108
+ veil.get("opscode_erchef", "sql_password")
109
+ else veil.exist?("postgresql", "sql_password")
110
+ veil.get("postgresql", "sql_password")
87
111
  end
88
112
  end
89
113
  end
@@ -145,6 +145,28 @@ class Chef
145
145
  end
146
146
  end
147
147
  end
148
+ purge_users_on_restore
149
+ end
150
+
151
+ def users_for_purge
152
+ purge_list = remote_user_list - local_user_list
153
+ # failsafe - don't delete pivotal
154
+ purge_list -= ['pivotal']
155
+ purge_list.each do |user|
156
+ yield user
157
+ end
158
+ end
159
+
160
+ def purge_users_on_restore
161
+ return unless config[:purge]
162
+ users_for_purge do |user|
163
+ ui.msg "Deleting user #{user} from remote (purge is on)"
164
+ begin
165
+ rest.delete("/users/#{user}")
166
+ rescue Net::HTTPServerException => e
167
+ ui.warn "Failed deleting user #{user} from remote #{e}"
168
+ end
169
+ end
148
170
  end
149
171
 
150
172
  def ec_key_import
@@ -229,11 +251,14 @@ class Chef
229
251
  proc { |entry| chef_fs_config.format_path(entry)})
230
252
  end
231
253
 
232
- Chef::Config.node_name = org_admin
254
+ Chef::Config.node_name = if config[:skip_version]
255
+ org_admin
256
+ else
257
+ server.supports_defaulting_to_pivotal? ? 'pivotal' : org_admin
258
+ end
233
259
 
234
260
  # Restore the entire org skipping the admin data and restoring groups and acls last
235
261
  ui.msg "Restoring the rest of the org"
236
- Chef::Log.debug "Using admin user: #{org_admin}"
237
262
  chef_fs_config = Chef::ChefFS::Config.new
238
263
  top_level_paths = chef_fs_config.local_fs.children.select { |entry| entry.name != 'acls' && entry.name != 'groups' }.map { |entry| entry.path }
239
264
 
@@ -34,5 +34,9 @@ class Chef
34
34
  rescue
35
35
  false
36
36
  end
37
+
38
+ def supports_defaulting_to_pivotal?
39
+ version >= Gem::Version.new('12.1.0')
40
+ end
37
41
  end
38
42
  end
@@ -1,3 +1,3 @@
1
1
  module KnifeECBackup
2
- VERSION = '2.0.7'
2
+ VERSION = '2.1.0'
3
3
  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.0.7
4
+ version: 2.1.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-02-09 00:00:00.000000000 Z
11
+ date: 2017-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '11.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: veil
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: Backup and Restore of Enterprise Chef
56
70
  email: jkeiser@opscode.com
57
71
  executables: []
@@ -102,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
116
  version: '0'
103
117
  requirements: []
104
118
  rubyforge_project:
105
- rubygems_version: 2.6.10
119
+ rubygems_version: 2.6.8
106
120
  signing_key:
107
121
  specification_version: 4
108
122
  summary: Backup and Restore of Enterprise Chef