knife-ec-backup 2.0.7 → 2.1.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: 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