knife-ec-backup 0.9.3 → 0.9.4
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 +4 -4
- data/lib/chef/knife/ec_backup.rb +24 -7
- data/lib/chef/knife/ec_restore.rb +186 -0
- data/lib/knife_ec_backup/version.rb +1 -1
- metadata +2 -2
- data/lib/chef/knife/opc_restore.rb +0 -96
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c285a56c4950610acb55aa11b686076df1a82b2
|
4
|
+
data.tar.gz: 5e6b0e50898af2481631db6703072bfd9708621a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0422cabb085a15ddf31b14a0eb53e169c184844a8ccd11091d2c8678355130d4fd99adde97ed4afb9a880b56652347b0f1b1ab948b5ab3e293234e59a37929b
|
7
|
+
data.tar.gz: 4fa91e9ebb1a1b5f5ebe8f1b7e03adb692b8e53cf8f897cc973e811ee9b48c242d046164f7547c7e5981aed050f3f469b9c0b4a3edf7d1dd0ff2f8b7b2fbb26d
|
data/lib/chef/knife/ec_backup.rb
CHANGED
@@ -33,10 +33,10 @@ class Chef
|
|
33
33
|
|
34
34
|
rest.get_rest('/users').each_pair do |name, url|
|
35
35
|
File.open("#{dest_dir}/users/#{name}.json", 'w') do |file|
|
36
|
-
file.write(rest.get_rest(url)
|
36
|
+
file.write(Chef::JSONCompat.to_json_pretty(rest.get_rest(url)))
|
37
37
|
end
|
38
38
|
File.open("#{dest_dir}/user_acls/#{name}.json", 'w') do |file|
|
39
|
-
file.write(user_acl_rest.get_rest("users/#{name}/_acl")
|
39
|
+
file.write(Chef::JSONCompat.to_json_pretty(user_acl_rest.get_rest("users/#{name}/_acl")))
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -46,10 +46,17 @@ class Chef
|
|
46
46
|
org = rest.get_rest(url)
|
47
47
|
if org['assigned_at']
|
48
48
|
puts "Grabbing organization #{name} ..."
|
49
|
-
|
50
|
-
file.write(org.to_json)
|
51
|
-
end
|
49
|
+
ensure_dir("#{dest_dir}/organizations/#{name}")
|
52
50
|
download_org(dest_dir, webui_key, name)
|
51
|
+
File.open("#{dest_dir}/organizations/#{name}/org.json", 'w') do |file|
|
52
|
+
file.write(Chef::JSONCompat.to_json_pretty(org))
|
53
|
+
end
|
54
|
+
File.open("#{dest_dir}/organizations/#{name}/members.json", 'w') do |file|
|
55
|
+
file.write(Chef::JSONCompat.to_json_pretty(rest.get_rest("#{url}/users")))
|
56
|
+
end
|
57
|
+
File.open("#{dest_dir}/organizations/#{name}/invitations.json", 'w') do |file|
|
58
|
+
file.write(Chef::JSONCompat.to_json_pretty(rest.get_rest("#{url}/association_requests")))
|
59
|
+
end
|
53
60
|
end
|
54
61
|
end
|
55
62
|
|
@@ -80,6 +87,17 @@ class Chef
|
|
80
87
|
|
81
88
|
Chef::Config.chef_server_url = "#{Chef::Config.chef_server_url}/organizations/#{name}"
|
82
89
|
|
90
|
+
ensure_dir(Chef::Config.chef_repo_path)
|
91
|
+
|
92
|
+
# Download the billing-admins acls as pivotal
|
93
|
+
chef_fs_config = ::ChefFS::Config.new
|
94
|
+
%w(/acls/groups/billing-admins.json).each do |name|
|
95
|
+
pattern = ::ChefFS::FilePattern.new(name)
|
96
|
+
if ::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.local_fs, chef_fs_config.chef_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
|
97
|
+
@error = true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
83
101
|
# Figure out who the admin is so we can spoof him and retrieve his stuff
|
84
102
|
rest = Chef::REST.new(Chef::Config.chef_server_url)
|
85
103
|
admin_users = rest.get_rest('groups/admins')['users']
|
@@ -90,8 +108,7 @@ class Chef
|
|
90
108
|
Chef::Config.custom_http_headers = (Chef::Config.custom_http_headers || {}).merge({'x-ops-request-source' => 'web'})
|
91
109
|
|
92
110
|
# Do the download
|
93
|
-
|
94
|
-
chef_fs_config ||= ::ChefFS::Config.new
|
111
|
+
chef_fs_config = ::ChefFS::Config.new
|
95
112
|
root_pattern = ::ChefFS::FilePattern.new('/')
|
96
113
|
if ::ChefFS::FileSystem.copy_to(root_pattern, chef_fs_config.chef_fs, chef_fs_config.local_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
|
97
114
|
@error = true
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class EcRestore < Chef::Knife
|
6
|
+
banner "knife ec restore"
|
7
|
+
|
8
|
+
deps do
|
9
|
+
require 'chef/json_compat'
|
10
|
+
require 'chef_fs/config'
|
11
|
+
require 'chef_fs/file_system'
|
12
|
+
require 'chef_fs/file_pattern'
|
13
|
+
require 'chef_fs/file_system/acl_entry'
|
14
|
+
require 'chef_fs/data_handler/acl_data_handler'
|
15
|
+
require 'securerandom'
|
16
|
+
require 'chef_fs/parallelizer'
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
if name_args.length == 0
|
21
|
+
ui.error("Must specify backup directory as argument.")
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
dest_dir = name_args[0]
|
26
|
+
webui_key = name_args[1]
|
27
|
+
rest = Chef::REST.new(Chef::Config.chef_server_url)
|
28
|
+
if name_args.length >= 3
|
29
|
+
user_acl_rest = Chef::REST.new(name_args[2])
|
30
|
+
else
|
31
|
+
user_acl_rest = rest
|
32
|
+
end
|
33
|
+
|
34
|
+
# Restore users
|
35
|
+
puts "Restoring users ..."
|
36
|
+
Dir.foreach("#{dest_dir}/users") do |filename|
|
37
|
+
next if filename !~ /(.+)\.json/
|
38
|
+
name = $1
|
39
|
+
user = JSONCompat.from_json(IO.read("#{dest_dir}/users/#{name}.json"))
|
40
|
+
begin
|
41
|
+
user_with_password = user.dup
|
42
|
+
user_with_password['password'] = SecureRandom.hex
|
43
|
+
rest.post_rest('users', user_with_password)
|
44
|
+
rescue Net::HTTPServerException => e
|
45
|
+
if e.response.code == "409"
|
46
|
+
rest.put_rest("users/#{name}", user)
|
47
|
+
else
|
48
|
+
raise
|
49
|
+
end
|
50
|
+
end
|
51
|
+
# Doesn't work at present
|
52
|
+
#user_acl = JSONCompat.from_json(IO.read("#{dest_dir}/user_acls/#{name}.json"))
|
53
|
+
#put_acl(user_acl_rest, "users/#{name}/_acl", user_acl)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Restore organizations
|
57
|
+
Dir.foreach("#{dest_dir}/organizations") do |name|
|
58
|
+
next if name == '..' || name == '.' || !File.directory?("#{dest_dir}/organizations/#{name}")
|
59
|
+
puts "Restoring org #{name} ..."
|
60
|
+
|
61
|
+
# Create organization
|
62
|
+
org = JSONCompat.from_json(IO.read("#{dest_dir}/organizations/#{name}/org.json"))
|
63
|
+
begin
|
64
|
+
rest.post_rest('organizations', org)
|
65
|
+
rescue Net::HTTPServerException => e
|
66
|
+
if e.response.code == "409"
|
67
|
+
rest.put_rest("organizations/#{name}", org)
|
68
|
+
else
|
69
|
+
raise
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Restore open invitations
|
74
|
+
invitations = JSONCompat.from_json(IO.read("#{dest_dir}/organizations/#{name}/invitations.json"))
|
75
|
+
parallelize(invitations) do |invitation|
|
76
|
+
begin
|
77
|
+
rest.post_rest("organizations/#{name}/association_requests", { 'user' => invitation['username'] })
|
78
|
+
rescue Net::HTTPServerException => e
|
79
|
+
if e.response.code != "409"
|
80
|
+
raise
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Repopulate org members
|
86
|
+
members = JSONCompat.from_json(IO.read("#{dest_dir}/organizations/#{name}/members.json"))
|
87
|
+
parallelize(members) do |member|
|
88
|
+
username = member['user']['username']
|
89
|
+
begin
|
90
|
+
response = rest.post_rest("organizations/#{name}/association_requests", { 'user' => username })
|
91
|
+
association_id = response["uri"].split("/").last
|
92
|
+
rest.put_rest("users/#{username}/association_requests/#{association_id}", { 'response' => 'accept' })
|
93
|
+
rescue Net::HTTPServerException => e
|
94
|
+
if e.response.code != "409"
|
95
|
+
raise
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Upload org data
|
101
|
+
upload_org(dest_dir, webui_key, name)
|
102
|
+
end
|
103
|
+
|
104
|
+
if @error
|
105
|
+
exit 1
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
PATHS = %w(chef_repo_path cookbook_path environment_path data_bag_path role_path node_path client_path acl_path group_path container_path)
|
110
|
+
CONFIG_VARS = %w(chef_server_url custom_http_headers node_name client_key) + PATHS
|
111
|
+
def upload_org(dest_dir, webui_key, name)
|
112
|
+
old_config = {}
|
113
|
+
CONFIG_VARS.each do |key|
|
114
|
+
old_config[key] = Chef::Config[key]
|
115
|
+
end
|
116
|
+
begin
|
117
|
+
# Clear out paths
|
118
|
+
PATHS.each do |path_var|
|
119
|
+
Chef::Config[path_var] = nil
|
120
|
+
end
|
121
|
+
Chef::Config.chef_repo_path = "#{dest_dir}/organizations/#{name}"
|
122
|
+
|
123
|
+
Chef::Config.chef_server_url = "#{Chef::Config.chef_server_url}/organizations/#{name}"
|
124
|
+
|
125
|
+
# Upload the admins group and billing-admins acls
|
126
|
+
chef_fs_config = ::ChefFS::Config.new
|
127
|
+
%w(/groups/admins.json /groups/billing-admins.json /acls/groups/billing-admins.json).each do |name|
|
128
|
+
pattern = ::ChefFS::FilePattern.new(name)
|
129
|
+
if ::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.local_fs, chef_fs_config.chef_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
|
130
|
+
@error = true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Figure out who the admin is so we can spoof him and retrieve his stuff
|
135
|
+
rest = Chef::REST.new(Chef::Config.chef_server_url)
|
136
|
+
admin_users = rest.get_rest('groups/admins')['users']
|
137
|
+
org_members = rest.get_rest('users').map { |user| user['user']['username'] }
|
138
|
+
admin_users.delete_if { |user| !org_members.include?(user) }
|
139
|
+
Chef::Config.node_name = admin_users[0]
|
140
|
+
Chef::Config.client_key = webui_key
|
141
|
+
Chef::Config.custom_http_headers = (Chef::Config.custom_http_headers || {}).merge({'x-ops-request-source' => 'web'})
|
142
|
+
|
143
|
+
# Do the upload.
|
144
|
+
chef_fs_config = ::ChefFS::Config.new
|
145
|
+
# groups and acls come last.
|
146
|
+
children = chef_fs_config.chef_fs.children.map { |child| child.name }
|
147
|
+
children.delete('acls')
|
148
|
+
children.delete('groups')
|
149
|
+
parallelize(children) do |child_name|
|
150
|
+
pattern = ::ChefFS::FilePattern.new("/#{child_name}")
|
151
|
+
if ::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.local_fs, chef_fs_config.chef_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
|
152
|
+
@error = true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
pattern = ::ChefFS::FilePattern.new("/groups")
|
156
|
+
if ::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.local_fs, chef_fs_config.chef_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
|
157
|
+
@error = true
|
158
|
+
end
|
159
|
+
pattern = ::ChefFS::FilePattern.new("/acls")
|
160
|
+
if ::ChefFS::FileSystem.copy_to(pattern, chef_fs_config.local_fs, chef_fs_config.chef_fs, nil, config, ui, proc { |entry| chef_fs_config.format_path(entry) })
|
161
|
+
@error = true
|
162
|
+
end
|
163
|
+
ensure
|
164
|
+
CONFIG_VARS.each do |key|
|
165
|
+
Chef::Config[key] = old_config[key]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def parallelize(entries, options = {}, &block)
|
171
|
+
::ChefFS::Parallelizer.parallelize(entries, options, &block)
|
172
|
+
end
|
173
|
+
|
174
|
+
def put_acl(rest, url, acls)
|
175
|
+
old_acls = rest.get_rest(url)
|
176
|
+
old_acls = ::ChefFS::DataHandler::AclDataHandler.new.normalize(old_acls, nil)
|
177
|
+
acls = ::ChefFS::DataHandler::AclDataHandler.new.normalize(acls, nil)
|
178
|
+
if acls != old_acls
|
179
|
+
::ChefFS::FileSystem::AclEntry::PERMISSIONS.each do |permission|
|
180
|
+
rest.put_rest("#{url}/#{permission}", { permission => acls[permission] })
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-ec-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Keiser
|
@@ -50,7 +50,7 @@ files:
|
|
50
50
|
- README.md
|
51
51
|
- Rakefile
|
52
52
|
- lib/chef/knife/ec_backup.rb
|
53
|
-
- lib/chef/knife/
|
53
|
+
- lib/chef/knife/ec_restore.rb
|
54
54
|
- lib/knife_ec_backup/version.rb
|
55
55
|
homepage: http://www.opscode.com
|
56
56
|
licenses:
|
@@ -1,96 +0,0 @@
|
|
1
|
-
require 'chef/knife'
|
2
|
-
|
3
|
-
class Chef
|
4
|
-
class Knife
|
5
|
-
class EcRestore < Chef::Knife
|
6
|
-
banner "knife ec restore"
|
7
|
-
|
8
|
-
deps do
|
9
|
-
require 'chef_fs/config'
|
10
|
-
require 'chef_fs/file_system'
|
11
|
-
require 'chef_fs/file_pattern'
|
12
|
-
end
|
13
|
-
|
14
|
-
def run
|
15
|
-
if name_args.length == 0
|
16
|
-
ui.error("Must specify backup directory as argument.")
|
17
|
-
exit 1
|
18
|
-
end
|
19
|
-
|
20
|
-
dest_dir = name_args[0]
|
21
|
-
webui_key = name_args[1]
|
22
|
-
rest = Chef::REST.new(Chef::Config.chef_server_url)
|
23
|
-
|
24
|
-
# Restore users
|
25
|
-
puts "Restoring users ..."
|
26
|
-
Dir.foreach("#{dest_dir}/users") do |user_file|
|
27
|
-
next if user_file !~ /(.+)\.json$/
|
28
|
-
name = $1
|
29
|
-
rest.post("#{dest_dir}/users", JSONCompat.to_json(IO.read("#{dest_dir}/users/#{user_file}")))
|
30
|
-
end
|
31
|
-
|
32
|
-
# Download organizations
|
33
|
-
Dir.foreach("#{dest_dir}/organizations") do |org_file|
|
34
|
-
next if org_file !~ /(.+)\.json/
|
35
|
-
name = $1
|
36
|
-
rest.post("#{dest_dir}/organizations", JSONCompat.to_json(IO.read("#{dest_dir}/users/#{user_file}")))
|
37
|
-
end
|
38
|
-
rest.get('/organizations').each_pair do |name, url|
|
39
|
-
org = rest.get(url)
|
40
|
-
if org['assigned_at']
|
41
|
-
puts "Restoring organization #{name} ..."
|
42
|
-
File.open("#{dest_dir}/organizations/#{name}.json", 'w') do |file|
|
43
|
-
file.write(org.to_json)
|
44
|
-
end
|
45
|
-
download_org(dest_dir, webui_key, name)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
if @error
|
50
|
-
exit 1
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def ensure_dir(dir)
|
55
|
-
if !File.exist?(dir)
|
56
|
-
Dir.mkdir(dir)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def download_org(dest_dir, webui_key, name)
|
61
|
-
@old_chef_server_url = Chef::Config.chef_server_url
|
62
|
-
@old_chef_repo_path = Chef::Config.chef_repo_path
|
63
|
-
@old_node_name = Chef::Config.node_name
|
64
|
-
@old_custom_http_headers = Chef::Config.custom_http_headers
|
65
|
-
@old_client_key = Chef::Config.client_key
|
66
|
-
begin
|
67
|
-
Chef::Config.chef_server_url = "#{Chef::Config.chef_server_url}/organizations/#{name}"
|
68
|
-
Chef::Config.chef_repo_path = "#{dest_dir}/organizations/#{name}"
|
69
|
-
|
70
|
-
# Figure out who the admin is so we can spoof him and retrieve his stuff
|
71
|
-
admin_users = rest.get('groups/admins')['users']
|
72
|
-
org_members = rest.get('users').map { |user| user['user']['username'] }
|
73
|
-
admin_users.delete_if { |user| !org_members.include?(user) }
|
74
|
-
admin = admin_users[0]
|
75
|
-
Chef::Config.node_name = admin_users[0]
|
76
|
-
Chef::Config.client_key = webui_key
|
77
|
-
Chef::Config.custom_http_headers = (Chef::Config.custom_http_headers || {}).merge({'x-ops-request-source' => 'web'})
|
78
|
-
|
79
|
-
# Do the download
|
80
|
-
ensure_dir(Chef::Config.chef_repo_path)
|
81
|
-
@chef_fs_config ||= ::ChefFS::Config.new
|
82
|
-
root_pattern = ::ChefFS::FilePattern.new('/')
|
83
|
-
if ::ChefFS::FileSystem.copy_to(root_pattern, @chef_fs_config.chef_fs, @chef_fs_config.local_fs, nil, config, ui, proc { |entry| @chef_fs_config.format_path(entry) })
|
84
|
-
@error = true
|
85
|
-
end
|
86
|
-
ensure
|
87
|
-
Chef::Config.chef_server_url = @old_chef_server_url
|
88
|
-
Chef::Config.chef_repo_path = @old_chef_repo_path
|
89
|
-
Chef::Config.node_name = @old_node_name
|
90
|
-
Chef::Config.custom_http_headers = @old_custom_http_headers
|
91
|
-
Chef::Config.client_key = @old_client_key
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|