knife-ec-backup 2.0.0.beta.2 → 2.0.0.beta.3

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: 6a427844677521bea76fb22774f405ae3ed97af6
4
- data.tar.gz: ac92e509e8ecee7a4e14852f99c5e43a72305306
3
+ metadata.gz: 2078c6252a1d006cfb49b5f6c3c2a7fc8e47ad30
4
+ data.tar.gz: b67b59762f716a2b66677d56beb86de993020682
5
5
  SHA512:
6
- metadata.gz: 4f27a84674dc02eaee070700efc7f8bf4e898710342d1059b3881deb6c4bd9b75b4aef109708604364a0188cf65a7c67b665460b08c47e019c3abdf77ca0a8a3
7
- data.tar.gz: ad983d25da97492db691b431ac6fef6f60f468d314ea2909f74132eec633053a8a26cd88b03890703d6fa86f8bd133b7f9ca77091fccda2fdb603406682ff817
6
+ metadata.gz: ad00c21eb5460532a51301a12d91c786aca6c8cd77b745024b639a4bcdf082b8fb25e416bc68eee84a4b1bcd942e0d3915dfd0e5dc2fba69a4971c372bb9451f
7
+ data.tar.gz: 091c6bf72543e5d197c361f906d998904d89dcc7e22a5935b88fc170565f5cc303e6cad38b25ef4a512a8889647bc1bfca739cd9772f6853c965cc1c3ecefcf6
data/README.md CHANGED
@@ -2,7 +2,14 @@
2
2
 
3
3
  # Description
4
4
 
5
- This is an UNOFFICIAL and EXPERIMENTAL knife plugin intended to back up and restore an entire Enterprise Chef / Private Chef server, preserving the data in an intermediate, editable text format.
5
+ knife-ec-backup can backup and restore the data in an Enterprise Chef
6
+ Server installation, preserving the data in an intermediate, editable
7
+ text format. It is similar to the `knife download` and `knife upload`
8
+ commands and uses the same underlying libraries, but also includes
9
+ workarounds for objects not yet supported by those tools and various
10
+ Server API deficiencies. The long-run goal is to improve `knife
11
+ donwload`, `knife upload` and the Chef Server API and deprecate this
12
+ tool.
6
13
 
7
14
  # Requirements
8
15
 
@@ -20,6 +27,13 @@ This will install the plugin directly on the Chef Server:
20
27
 
21
28
  /opt/opscode/embedded/bin/gem install knife-ec-backup
22
29
 
30
+ The latest versions of knife-ec-backup require gems with native
31
+ extensions, thus you must install a standard build toolchain. To
32
+ install knife-ec-backup without installing libpq development headers
33
+ on your system, try the following:
34
+
35
+ /opt/opscode/embedded/bin/gem install knife-ec-backup -- --with-pg-config=/opt/opscode/embedded/postgresql/9.2/bin/pg_config
36
+
23
37
  ## Build from source
24
38
  Clone the git repository and run the following from inside:
25
39
 
@@ -29,22 +43,61 @@ Clone the git repository and run the following from inside:
29
43
  # Configuration
30
44
 
31
45
  ## Permissions
46
+
32
47
  Note that most users in an EC installation lack the permissions to pull all of the data from all organizations and other users.
33
48
  This plugin **REQUIRES THE PIVOTAL KEY AND WEBUI KEY** from the Chef Server.
34
49
  It is recommended that you run this from a frontend Enterprise Chef Server, you can use --user and --key to pass the pivotal information along.
35
50
 
36
51
  # Subcommands
37
52
 
53
+ ## Common Option
54
+
55
+ The following options are supported across all subcommands:
56
+
57
+ * `--sql_host`:
58
+ The hostname of the Chef Server's postgresql server. (default: localhost)
59
+
60
+ * `--sql_port`:
61
+ The postgresql listening port on the Chef Server. (default: 5432)
62
+
63
+ * `--sql_user`:
64
+ The username of postgresql user with access to the opscode_chef
65
+ database. (default: autoconfigured from
66
+ /etc/opscode/chef-server-running.json)
67
+
68
+ * `--sql_password`:
69
+ The password for the sql_user. (default: autoconfigured from /etc/opscode/chef-server-running.json)
70
+
38
71
  ## knife ec backup DEST_DIR (options)
39
72
 
40
73
  *Options*
41
74
 
75
+ * `--concurrency THREAD_COUNT`:
76
+ The maximum number of concurrent requests to make to the Chef
77
+ Server. (default: 10)
78
+
42
79
  * `--webui-key`:
43
80
  Used to set the path to the WebUI Key (default: /etc/opscode/webui_priv.pem)
81
+ skip any auto-configured options (default: false)
82
+
83
+ * `--with-user-sql`:
84
+ Whether to backup/restore user data directly from the database. This
85
+ requires access to the listening postgresql port on the Chef
86
+ Server. This is required to correctly handle user passwords and
87
+ to ensure user-specific association groups are not duplicated.
88
+
44
89
  * `--skip-useracl`:
45
- Whether to skip downloading User ACLs. This is required for EC 11.0.0 and lower (default: false)
46
- * `--skip-version-check'`:
47
- Whether to skip checking the Chef Server version. This will also skip any auto-configured options (default: false)
90
+ Skip download/restore of the user ACLs. User ACLs are the
91
+ permissions that actors have *on other global users*. These are
92
+ not the ACLs that control what permissions users have on various
93
+ Chef objects.
94
+
95
+ * `--skip-version-check`:
96
+ Skip Chef Server version check. This will also skip any auto-configured options (default: false)
97
+
98
+ * `--only-org ORG`:
99
+ Only donwload/restore objects in the named organization. Global
100
+ objects such as users will still be downloaded/restored.
48
101
 
49
102
  Creates a repository of an entire Enterprise Chef / Private Chef server.
50
103
 
@@ -100,24 +153,82 @@ This compares very closely with the "knife download /" from an OSC server:
100
153
 
101
154
  ## knife ec restore DEST_DIR (options)
102
155
 
156
+ Restores all data from the specified DEST_DIR to an Enterprise Chef /
157
+ Private Chef server. DEST_DIR should be a backup directory created by
158
+ `knife ec restore`
159
+
103
160
  *Options*
104
161
 
105
162
  * `--webui-key`:
106
163
  Used to set the path to the WebUI Key (default: /etc/opscode/webui_priv.pem)
164
+
107
165
  * `--overwrite-pivotal`:
108
- Whether to overwrite pivotal's key. Once this is done, future requests will fail until you fix the private key (default: false)
166
+ Whether to overwrite pivotal's key. Once this is done, future
167
+ requests will fail until you fix the private key (default: false)
168
+
169
+ * `--skip-users`:
170
+ Skip the restore of global users. This may cause organization
171
+ uploading to fail if the necessary users do not exist on the Chef
172
+ Server.
173
+
174
+ * `--concurrency THREAD_COUNT`:
175
+ The maximum number of concurrent requests to make to the Chef
176
+ Server. (default: 10)
177
+
178
+ * `--skip-version-check`:
179
+ Skip Chef Server version check. This will
180
+ also skip any auto-configured options (default: false)
181
+
182
+ * `--with-user-sql`:
183
+ Whether to backup/restore user data directly from the database. This
184
+ requires access to the listening postgresql port on the Chef
185
+ Server. This is required to correctly handle user passwords and
186
+ to ensure user-specific association groups are not
187
+ duplicated. This option will only work on `restore` if it was also
188
+ used during the `backup`.
189
+
109
190
  * `--skip-useracl`:
110
- Whether to skip downloading User ACLs. This is required for EC 11.0.0 and lower (default: false)
111
- * `--skip-version-check'`:
112
- Whether to skip checking the Chef Server version. This will also skip any auto-configured options (default: false)
191
+ Skip download/restore of the user ACLs. User ACLs are the
192
+ permissions that actors have *on other global users*. These are
193
+ not the ACLs that control what permissions users have on various
194
+ Chef objects.
195
+
196
+ * `--only-org ORG`:
197
+ Only donwload/restore objects in the named organization. Global
198
+ objects such as users will still be downloaded/restored.
199
+
200
+ ## knife ec key export [FILENAME]
201
+
202
+ Create a json representation of the users table from the Chef Server
203
+ database. If no argument is given, the name of the backup is
204
+ `key_dump.json`.
205
+
206
+ Please note, most users should use `knife ec backup` with the
207
+ `--with-user-sql` option rather than this command.
208
+
209
+ ## knife ec key import [FILENAME]
210
+
211
+ Import a json representation of the users table from FILENAME to the
212
+ the Chef Server database. If no argument is given, the filename is
213
+ assumed to be `key_dump.json`.
214
+
215
+ Please note, most user should use `knife ec restore` with the
216
+ `--with-user-sql` option rather than this command.
217
+
218
+ # Known Bugs
219
+
220
+ - `knife ec restore` can fail to restore cookbooks, failing with an
221
+ internal server error. A common cause of this problem is a
222
+ concurrency bug in Chef Server. Setting `--concurrency 1` can often
223
+ work around the issue.
113
224
 
114
- Restores all data from a repository to an Enterprise Chef / Private Chef server.
225
+ - `knife ec restore` can fail if the pool of pre-created organizations
226
+ can not keep up with the newly created organizations. This can
227
+ typically be resolved simply be restarting the restore. To avoid
228
+ this error for backups with large number of organizations, try
229
+ setting (in /etc/opscode/private-chef.rb):
115
230
 
116
- # TODO
231
+ opscode_org_creator['ready_org_depth']
117
232
 
118
- * Ensure easy installation into embedded ruby gemset on Chef Server.
119
- * Remove requirement for Knife Essentials gem to be installed.
120
- * Single org backups.
121
- * This plugin does **NOT** currently backup user passwords. **They will have to be reset after a restore.**
122
- * This plugin does **NOT** currently restore user public keys. **Private keys will have to be reset after a restore.**
123
- * This plugin does **NOT** currently restore custom user ACLs. **It will revert back to default ACLs on a restore.**
233
+ to the number of organizations in your backup and waiting for the
234
+ pool to fill before running `knife ec restore`
@@ -1,214 +1,133 @@
1
1
  require 'chef/knife'
2
+ require 'chef/knife/ec_base'
2
3
 
3
4
  class Chef
4
5
  class Knife
5
6
  class EcBackup < Chef::Knife
6
- banner "knife ec backup DIRECTORY"
7
-
8
- option :concurrency,
9
- :long => '--concurrency THREADS',
10
- :description => 'Maximum number of simultaneous requests to send (default: 10)'
11
-
12
- option :webui_key,
13
- :long => '--webui-key KEYPATH',
14
- :description => 'Used to set the path to the WebUI Key (default: /etc/opscode/webui_priv.pem)'
15
-
16
- option :skip_useracl,
17
- :long => '--skip-useracl',
18
- :boolean => true,
19
- :default => false,
20
- :description => "Whether to skip downloading User ACLs. This is required for EC 11.0.0 and lower"
21
-
22
- option :skip_version,
23
- :long => '--skip-version-check',
24
- :boolean => true,
25
- :default => false,
26
- :description => "Whether to skip checking the Chef Server version. This will also skip any auto-configured options"
27
-
28
- option :with_user_sql,
29
- :long => '--with-user-sql',
30
- :description => 'Whether to try direct data base access for user export. Required to properly handle passwords, keys, and USAGs'
31
-
32
- option :org,
33
- :long => '--only-org ORGNAME',
34
- :description => "Only back up objects in the named organization (default: all orgs)"
35
-
36
- option :sql_host,
37
- :long => '--sql-host HOSTNAME',
38
- :description => 'Postgresql database hostname (default: localhost)',
39
- :default => "localhost"
40
7
 
41
- option :sql_port,
42
- :long => '--sql-port PORT',
43
- :description => 'Postgresql database port (default: 5432)',
44
- :default => 5432
45
-
46
- option :sql_user,
47
- :long => "--sql-user USERNAME",
48
- :description => 'User used to connect to the postgresql database.'
49
-
50
- option :sql_password,
51
- :long => "--sql-password PASSWORD",
52
- :description => 'Password used to connect to the postgresql database'
8
+ include Knife::EcBase
53
9
 
10
+ banner "knife ec backup DIRECTORY"
54
11
 
55
12
  deps do
56
13
  require 'chef/chef_fs/config'
57
14
  require 'chef/chef_fs/file_system'
58
15
  require 'chef/chef_fs/file_pattern'
59
16
  require 'chef/chef_fs/parallelizer'
60
- end
61
-
62
- def configure_chef
63
- super
64
- Chef::Config[:concurrency] = config[:concurrency].to_i if config[:concurrency]
65
- Chef::ChefFS::Parallelizer.threads = (Chef::Config[:concurrency] || 10) - 1
17
+ require 'chef/server'
18
+ require 'fileutils'
66
19
  end
67
20
 
68
21
  def run
69
- #Check for destination directory argument
70
- if name_args.length <= 0
71
- ui.error("Must specify backup directory as an argument.")
72
- exit 1
73
- end
74
- dest_dir = name_args[0]
75
-
22
+ set_dest_dir_from_args!
23
+ set_client_config!
24
+ set_skip_user_acl!
25
+ ensure_webui_key_exists!
76
26
 
77
- #Check for pivotal user and key
78
- node_name = Chef::Config.node_name
79
- client_key = Chef::Config.client_key
80
- if node_name != "pivotal"
81
- if !File.exist?("/etc/opscode/pivotal.pem")
82
- ui.error("Username not configured as pivotal and /etc/opscode/pivotal.pem does not exist. It is recommended that you run this plugin from your Chef server.")
83
- exit 1
27
+ for_each_user do |username, url|
28
+ download_user(username, url)
29
+ if config[:skip_useracl]
30
+ ui.warn("Skipping user ACL download for #{username}. To download this ACL, remove --skip-useracl or upgrade your Enterprise Chef Server.")
31
+ else
32
+ download_user_acl(username)
84
33
  end
85
- Chef::Config.node_name = 'pivotal'
86
- Chef::Config.client_key = '/etc/opscode/pivotal.pem'
87
34
  end
88
35
 
89
- #Check for WebUI Key
90
- if config[:webui_key] == nil
91
- if !File.exist?("/etc/opscode/webui_priv.pem")
92
- ui.error("WebUI not specified and /etc/opscode/webui_priv.pem does not exist. It is recommended that you run this plugin from your Chef server.")
93
- exit 1
94
- end
95
- ui.warn("WebUI not specified. Using /etc/opscode/webui_priv.pem")
96
- webui_key = '/etc/opscode/webui_priv.pem'
97
- else
98
- webui_key = config[:webui_key]
36
+ if config[:with_user_sql]
37
+ export_users_from_sql
99
38
  end
100
39
 
101
- #Set the server root
102
- server_root = Chef::Config.chef_server_root
103
- if server_root == nil
104
- server_root = Chef::Config.chef_server_url.gsub(/\/organizations\/+[^\/]+\/*$/, '')
105
- ui.warn("chef_server_root not found in knife configuration. Setting root to: #{server_root}")
106
- Chef::Config.chef_server_root = server_root
40
+ ensure_dir("#{dest_dir}/organizations")
41
+ for_each_organization do |org_object|
42
+ name = org_object['name']
43
+ write_org_object_to_disk(org_object)
44
+ download_org_data(name)
45
+ download_org_members(name)
46
+ download_org_invitations(name)
107
47
  end
48
+ end
108
49
 
109
- rest = Chef::REST.new(Chef::Config.chef_server_root)
110
-
111
- if config[:skip_version] && config[:skip_useracl]
112
- ui.warn("Skipping the Chef Server version check. This will also skip any auto-configured options")
113
- user_acl_rest = nil
114
- elsif config[:skip_version] && !config[:skip_useracl]
115
- ui.warn("Skipping the Chef Server version check. This will also skip any auto-configured options")
116
- user_acl_rest = rest
117
- else # Grab Chef Server version number so that we can auto set options
118
- uri = URI.parse("#{Chef::Config.chef_server_root}/version")
119
- server_version = open(uri, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE}).each_line.first.split(' ').last
120
- server_version_parts = server_version.split('.')
121
-
122
- if server_version_parts.count == 3
123
- puts "Detected Enterprise Chef Server version: #{server_version}"
124
-
125
- # All versions of Chef Server below 11.0.1 are missing the GET User ACL helper in nginx
126
- if server_version_parts[0].to_i < 11 || (server_version_parts[0].to_i == 11 && server_version_parts[1].to_i == 0 && server_version_parts[0].to_i < 1)
127
- #Check to see if Opscode-Account can be directly from the local machine
128
- begin
129
- user_acl_rest.get('users')
130
- ui.warn("Your version of Enterprise Chef Server does not support the downloading of User ACLs. Using local connection to backup")
131
- user_acl_rest = Chef::REST.new("http://127.0.0.1:9465")
132
- rescue
133
- ui.warn("Your version of Enterprise Chef Server does not support the downloading of User ACLs. Setting skip-useracl to TRUE")
134
- config[:skip_useracl] = true
135
- user_acl_rest = nil
136
- end
137
- else
138
- user_acl_rest = rest
139
- end
50
+ def for_each_user
51
+ rest.get_rest('/users').each_pair do |name, url|
52
+ yield name, url
53
+ end
54
+ end
140
55
 
56
+ def for_each_organization
57
+ rest.get_rest('/organizations').each_pair do |name, url|
58
+ next unless (config[:org].nil? || config[:org] == name)
59
+ ui.msg "Downloading organization object for #{name} from #{url}"
60
+ org = rest.get_rest(url)
61
+ # Enterprise Chef 11 and below uses a pool of precreated
62
+ # organizations to account for slow organization creation
63
+ # using CouchDB. Thus, on server versions < 12 we want to
64
+ # skip any of these precreated organizations by checking if
65
+ # they have been assigned or not. The Chef 12 API does not
66
+ # return an assigned_at field.
67
+ if org['assigned_at'] || server.version >= Gem::Version.new("12")
68
+ yield org
141
69
  else
142
- ui.warn("Unable to detect Chef Server version.")
70
+ ui.msg "Skipping pre-created org #{name}"
143
71
  end
144
72
  end
73
+ end
145
74
 
146
- # Grab users
147
- puts "Grabbing users ..."
75
+ def download_user(username, url)
148
76
  ensure_dir("#{dest_dir}/users")
149
- ensure_dir("#{dest_dir}/user_acls")
150
-
151
- rest.get_rest('/users').each_pair do |name, url|
152
- File.open("#{dest_dir}/users/#{name}.json", 'w') do |file|
153
- file.write(Chef::JSONCompat.to_json_pretty(rest.get_rest(url)))
154
- end
155
-
156
- if config[:skip_useracl]
157
- ui.warn("Skipping user ACL download for #{name}. To download this ACL, remove --skip-useracl or upgrade your Enterprise Chef Server.")
158
- next
159
- end
77
+ File.open("#{dest_dir}/users/#{username}.json", 'w') do |file|
78
+ file.write(Chef::JSONCompat.to_json_pretty(rest.get_rest(url)))
79
+ end
80
+ end
160
81
 
161
- File.open("#{dest_dir}/user_acls/#{name}.json", 'w') do |file|
162
- file.write(Chef::JSONCompat.to_json_pretty(user_acl_rest.get_rest("users/#{name}/_acl")))
163
- end
82
+ def download_user_acl(username)
83
+ ensure_dir("#{dest_dir}/user_acls")
84
+ File.open("#{dest_dir}/user_acls/#{username}.json", 'w') do |file|
85
+ file.write(Chef::JSONCompat.to_json_pretty(user_acl_rest.get_rest("users/#{username}/_acl")))
164
86
  end
87
+ end
165
88
 
166
- if config[:with_user_sql]
167
- require 'chef/knife/ec_key_export'
168
- Chef::Knife::EcKeyExport.deps
169
- k = Chef::Knife::EcKeyExport.new
170
- k.name_args = ["#{dest_dir}/key_dump.json"]
171
- k.config[:sql_host] = config[:sql_host]
172
- k.config[:sql_port] = config[:sql_port]
173
- k.config[:sql_user] = config[:sql_user]
174
- k.config[:sql_password] = config[:sql_password]
175
- k.run
89
+ def export_users_from_sql
90
+ require 'chef/knife/ec_key_export'
91
+ Chef::Knife::EcKeyExport.deps
92
+ k = Chef::Knife::EcKeyExport.new
93
+ k.name_args = ["#{dest_dir}/key_dump.json"]
94
+ k.config[:sql_host] = config[:sql_host]
95
+ k.config[:sql_port] = config[:sql_port]
96
+ k.config[:sql_user] = config[:sql_user]
97
+ k.config[:sql_password] = config[:sql_password]
98
+ k.run
99
+ end
100
+
101
+ def write_org_object_to_disk(org_object)
102
+ name = org_object['name']
103
+ ensure_dir("#{dest_dir}/organizations/#{name}")
104
+ File.open("#{dest_dir}/organizations/#{name}/org.json", 'w') do |file|
105
+ file.write(Chef::JSONCompat.to_json_pretty(org_object))
176
106
  end
107
+ end
177
108
 
178
- # Download organizations
179
- ensure_dir("#{dest_dir}/organizations")
180
- rest.get_rest('/organizations').each_pair do |name, url|
181
- do_org = (config[:org].nil? || config[:org] == name)
182
- org = rest.get_rest(url)
183
- if org['assigned_at'] and do_org
184
- puts "Grabbing organization #{name} ..."
185
- ensure_dir("#{dest_dir}/organizations/#{name}")
186
- download_org(dest_dir, webui_key, name)
187
- File.open("#{dest_dir}/organizations/#{name}/org.json", 'w') do |file|
188
- file.write(Chef::JSONCompat.to_json_pretty(org))
189
- end
190
- File.open("#{dest_dir}/organizations/#{name}/members.json", 'w') do |file|
191
- file.write(Chef::JSONCompat.to_json_pretty(rest.get_rest("#{url}/users")))
192
- end
193
- File.open("#{dest_dir}/organizations/#{name}/invitations.json", 'w') do |file|
194
- file.write(Chef::JSONCompat.to_json_pretty(rest.get_rest("#{url}/association_requests")))
195
- end
196
- end
109
+ def download_org_members(name)
110
+ ensure_dir("#{dest_dir}/organizations/#{name}")
111
+ File.open("#{dest_dir}/organizations/#{name}/members.json", 'w') do |file|
112
+ file.write(Chef::JSONCompat.to_json_pretty(rest.get_rest("/organizations/#{name}/users")))
197
113
  end
114
+ end
198
115
 
199
- if @error
200
- exit 1
116
+ def download_org_invitations(name)
117
+ ensure_dir("#{dest_dir}/organizations/#{name}")
118
+ File.open("#{dest_dir}/organizations/#{name}/invitations.json", 'w') do |file|
119
+ file.write(Chef::JSONCompat.to_json_pretty(rest.get_rest("/organizations/#{name}/association_requests")))
201
120
  end
202
121
  end
203
122
 
204
123
  def ensure_dir(dir)
205
124
  if !File.exist?(dir)
206
- Dir.mkdir(dir)
125
+ FileUtils.mkdir_p(dir)
207
126
  end
208
127
  end
209
128
 
210
129
  PATHS = %w(chef_repo_path cookbook_path environment_path data_bag_path role_path node_path client_path acl_path group_path container_path)
211
- def download_org(dest_dir, webui_key, name)
130
+ def download_org_data(name)
212
131
  old_config = Chef::Config.save
213
132
 
214
133
  begin
@@ -219,48 +138,47 @@ class Chef
219
138
  Chef::Config.chef_repo_path = "#{dest_dir}/organizations/#{name}"
220
139
  Chef::Config.versioned_cookbooks = true
221
140
 
222
- Chef::Config.chef_server_url = "#{Chef::Config.chef_server_root}/organizations/#{name}"
141
+ Chef::Config.chef_server_url = "#{server.root_url}/organizations/#{name}"
223
142
 
224
143
  ensure_dir(Chef::Config.chef_repo_path)
225
144
 
226
145
  # Download the billing-admins ACL and group as pivotal
227
146
  chef_fs_config = Chef::ChefFS::Config.new
228
- pattern = Chef::ChefFS::FilePattern.new('/acls/groups/billing-admins.json')
229
- if Chef::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.chef_fs, chef_fs_config.local_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
230
- @error = true
231
- end
232
- pattern = Chef::ChefFS::FilePattern.new('/groups/billing-admins.json')
233
- if Chef::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.chef_fs, chef_fs_config.local_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
234
- @error = true
235
- end
236
- pattern = Chef::ChefFS::FilePattern.new('/groups/admins.json')
237
- if Chef::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.chef_fs, chef_fs_config.local_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
238
- @error = true
239
- end
147
+ chef_fs_copy_pattern('/acls/groups/billing-admins.json', chef_fs_config)
148
+ chef_fs_copy_pattern('/groups/billing-admins.json', chef_fs_config)
149
+ chef_fs_copy_pattern('/groups/admins.json', chef_fs_config)
240
150
 
241
- # Figure out who the admin is so we can spoof him and retrieve his stuff
242
- rest = Chef::REST.new(Chef::Config.chef_server_url)
243
- admin_users = rest.get_rest('groups/admins')['users']
244
- org_members = rest.get_rest('users').map { |user| user['user']['username'] }
245
- admin_users.delete_if { |user| !org_members.include?(user) }
246
- Chef::Config.node_name = admin_users[0]
247
- Chef::Config.client_key = webui_key
248
- Chef::Config.custom_http_headers = (Chef::Config.custom_http_headers || {}).merge({'x-ops-request-source' => 'web'})
151
+ # Set Chef::Config to use an organization administrator
152
+ Chef::Config.node_name = org_admin
249
153
 
250
154
  # Download the entire org skipping the billing admins group ACL and the group itself
251
155
  chef_fs_config = Chef::ChefFS::Config.new
252
156
  top_level_paths = chef_fs_config.chef_fs.children.select { |entry| entry.name != 'acls' && entry.name != 'groups' }.map { |entry| entry.path }
253
- acl_paths = Chef::ChefFS::FileSystem.list(chef_fs_config.chef_fs, Chef::ChefFS::FilePattern.new('/acls/*')).select { |entry| entry.name != 'groups' }.map { |entry| entry.path }
254
- group_acl_paths = Chef::ChefFS::FileSystem.list(chef_fs_config.chef_fs, Chef::ChefFS::FilePattern.new('/acls/groups/*')).select { |entry| entry.name != 'billing-admins.json' }.map { |entry| entry.path }
255
- group_paths = Chef::ChefFS::FileSystem.list(chef_fs_config.chef_fs, Chef::ChefFS::FilePattern.new('/groups/*')).select { |entry| entry.name != 'billing-admins.json' }.map { |entry| entry.path }
157
+ acl_paths = chef_fs_paths('/acls/*', chef_fs_config, 'groups')
158
+ group_acl_paths = chef_fs_paths('/acls/groups/*', chef_fs_config, 'billing-admins.json')
159
+ group_paths = chef_fs_paths('/groups/*', chef_fs_config, 'billing-admins.json')
256
160
  (top_level_paths + group_acl_paths + acl_paths + group_paths).each do |path|
257
- Chef::ChefFS::FileSystem.copy_to(Chef::ChefFS::FilePattern.new(path), chef_fs_config.chef_fs, chef_fs_config.local_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
161
+ chef_fs_copy_pattern(path, chef_fs_config)
258
162
  end
259
-
260
163
  ensure
261
164
  Chef::Config.restore(old_config)
262
165
  end
263
166
  end
167
+
168
+ def chef_fs_paths(pattern_str, chef_fs_config, exclude=nil)
169
+ pattern = Chef::ChefFS::FilePattern.new(pattern_str)
170
+ list = Chef::ChefFS::FileSystem.list(chef_fs_config.chef_fs, pattern)
171
+ list = list.select { |entry| entry.name != exclude } if ! exclude.nil?
172
+ list.map {|entry| entry.path }
173
+ end
174
+
175
+ def chef_fs_copy_pattern(pattern_str, chef_fs_config)
176
+ pattern = Chef::ChefFS::FilePattern.new(pattern_str)
177
+ Chef::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.chef_fs,
178
+ chef_fs_config.local_fs, nil,
179
+ config, ui,
180
+ proc { |entry| chef_fs_config.format_path(entry) })
181
+ end
264
182
  end
265
183
  end
266
184
  end