knife-ec-backup 0.9.3 → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|