knife-backup 0.0.8 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +1 -1
- data/README.md +22 -8
- data/knife-backup.gemspec +1 -0
- data/lib/chef/knife/backup_export.rb +50 -11
- data/lib/chef/knife/backup_restore.rb +77 -15
- data/lib/knife-backup/version.rb +1 -1
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3b74fdf9a67ae00a32a68e1b37201dd7d27e318
|
4
|
+
data.tar.gz: 0cfa2a717cc5223e068424beab952813c250805a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 766023ac2102bc799dd8eb27af1bf53b3175ecb601007722411ca2ff88609c463ea390adf0f2df22a89e262e88699fc1e7ac9a99824a1e83ccadee8811548ef9
|
7
|
+
data.tar.gz: 2bd111ebe9cb9a58526293976d30a33ad92ca915ae69c7adbd164d9bb085fa49e13876945dafbbcbd4f406983263345cfd05a72585d0135396e4a31832dc2ae7
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,6 +4,7 @@ Knife-Backup
|
|
4
4
|
Knife-Backup is a [Knife](http://wiki.opscode.com/display/chef/Knife) plugin that can help you backup and restore a chef server. It is based on the great work of [Steven Danna][stevendanna] and [Joshua Timberman][jtimberman] on the [BackupExport][backup_export] and [BackupRestore][backup_restore] plugins. Currently knife-backup has support for the following objects:
|
5
5
|
|
6
6
|
* clients
|
7
|
+
* users (chef 11 - Open Source)
|
7
8
|
* nodes
|
8
9
|
* roles
|
9
10
|
* environments
|
@@ -12,7 +13,9 @@ Knife-Backup is a [Knife](http://wiki.opscode.com/display/chef/Knife) plugin tha
|
|
12
13
|
|
13
14
|
knife-backup will backup all cookbook versions available on the chef server. Cookbooks are normally available in a repository and should be easy to upload like that, but if you are using various cookbook versions in each environment then it might not be so trivial to find and upload them back to the server; downloading them and having them available to upload like that is simple and clean. If you have too many cookbook [versions](http://www.ducea.com/2013/02/26/knife-cleanup/) then you might want to cleanup them first using something like [knife-cleanup][knifecleanup]
|
14
15
|
|
15
|
-
|
16
|
+
Users are a bit tricky, knife-backup can't gather the crypted passwords via the chef server so it's forced to reset them to a random string on restore. Be sure to copy them from the restore output or reset them.
|
17
|
+
|
18
|
+
*Known limitation*: currently it is not possible to overwrite a client object already available on the target server and these will be skipped.
|
16
19
|
|
17
20
|
## Installation
|
18
21
|
|
@@ -24,25 +27,36 @@ gem install knife-backup
|
|
24
27
|
|
25
28
|
## Usage
|
26
29
|
|
27
|
-
|
30
|
+
Currently the available commands are:
|
28
31
|
|
29
32
|
```bash
|
30
|
-
knife backup
|
33
|
+
knife backup export [component component ...] [-D DIR] [options]
|
34
|
+
knife backup restore [component component ...] [-D DIR]
|
35
|
+
|
36
|
+
#Example:
|
37
|
+
knife backup export cookbooks roles environments -D ~/my_chef_backup
|
31
38
|
```
|
39
|
+
#### Optional Switches for export
|
32
40
|
|
33
|
-
|
41
|
+
- `-N`, `--latest` only download the latest version of a cookbook
|
42
|
+
- `-I`, `--ignore-permissions` ignore any permission errors during export
|
43
|
+
|
44
|
+
#### Optional Switches for restore
|
45
|
+
|
46
|
+
- `-I`, `--ignore-metadata-errors` Ignore json metadata errors when restoring cookbooks
|
47
|
+
|
48
|
+
|
49
|
+
For more information on commands:
|
34
50
|
|
35
51
|
```bash
|
36
|
-
knife backup
|
37
|
-
knife backup restore [-D DIR]
|
52
|
+
knife backup SUB-COMMAND --help
|
38
53
|
```
|
39
54
|
|
40
55
|
Note: you should treat this as beta software; I'm using it with success for my needs and hopefully you will find it useful too.
|
41
56
|
|
42
57
|
## Todo/Ideas
|
43
|
-
|
58
|
+
|
44
59
|
* Timestamp for the backup folder
|
45
|
-
* Limit the backup to just some objects (for ex, just backup the cookbooks or just the nodes)
|
46
60
|
* Track the failed downloads and report them at the end
|
47
61
|
* Find out if there is a way to overwrite a client object.
|
48
62
|
|
data/knife-backup.gemspec
CHANGED
@@ -10,6 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.homepage = "https://github.com/mdxp/knife-backup"
|
11
11
|
s.summary = %q{Chef knife plugins to help backup and restore chef servers}
|
12
12
|
s.description = s.summary
|
13
|
+
s.license = "Apache 2.0"
|
13
14
|
|
14
15
|
s.rubyforge_project = "knife-backup"
|
15
16
|
|
@@ -19,6 +19,10 @@
|
|
19
19
|
|
20
20
|
require 'chef/node'
|
21
21
|
require 'chef/api_client'
|
22
|
+
|
23
|
+
if Chef::VERSION =~ /^11\./
|
24
|
+
require 'chef/user'
|
25
|
+
end
|
22
26
|
require 'chef/knife/cookbook_download'
|
23
27
|
|
24
28
|
module ServerBackup
|
@@ -32,16 +36,22 @@ module ServerBackup
|
|
32
36
|
banner "knife backup export [COMPONENT [COMPONENT ...]] [-D DIR] (options)"
|
33
37
|
|
34
38
|
option :backup_dir,
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
:short => "-D DIR",
|
40
|
+
:long => "--backup-directory DIR",
|
41
|
+
:description => "Store backup data in DIR. DIR will be created if it does not already exist.",
|
42
|
+
:default => Chef::Config[:knife][:chef_server_backup_dir] ? Chef::Config[:knife][:chef_server_backup_dir] : File.join(".chef", "chef_server_backup")
|
39
43
|
|
40
44
|
option :latest,
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
+
:short => "-N",
|
46
|
+
:long => "--latest",
|
47
|
+
:description => "The version of the cookbook to download",
|
48
|
+
:boolean => true
|
49
|
+
|
50
|
+
option :ignore_perm,
|
51
|
+
:short => "-I",
|
52
|
+
:long => "--ignore-permissions",
|
53
|
+
:description => "Continue in case a permission problem occurs",
|
54
|
+
:boolean => true
|
45
55
|
|
46
56
|
def run
|
47
57
|
validate!
|
@@ -50,7 +60,8 @@ module ServerBackup
|
|
50
60
|
end
|
51
61
|
|
52
62
|
private
|
53
|
-
COMPONENTS = %w(clients nodes roles data_bags environments cookbooks)
|
63
|
+
COMPONENTS = %w(clients users nodes roles data_bags environments cookbooks)
|
64
|
+
LOAD_TRIES = 5
|
54
65
|
|
55
66
|
def validate!
|
56
67
|
bad_names = name_args - COMPONENTS
|
@@ -68,6 +79,14 @@ module ServerBackup
|
|
68
79
|
backup_standard("clients", Chef::ApiClient)
|
69
80
|
end
|
70
81
|
|
82
|
+
def users
|
83
|
+
if Chef::VERSION =~ /^1[1]\./
|
84
|
+
backup_standard("users", Chef::User)
|
85
|
+
else
|
86
|
+
ui.warn "users export only supported on chef == 11"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
71
90
|
def roles
|
72
91
|
backup_standard("roles", Chef::Role)
|
73
92
|
end
|
@@ -99,14 +118,34 @@ module ServerBackup
|
|
99
118
|
klass.list.each do |component_name, url|
|
100
119
|
next if component == "environments" && component_name == "_default"
|
101
120
|
ui.msg "Backing up #{component} #{component_name}"
|
102
|
-
component_obj = klass
|
121
|
+
component_obj = load_object(klass, component_name).to_hash
|
122
|
+
unless component_obj
|
123
|
+
ui.error "Could not load #{klass} #{component_name}."
|
124
|
+
next
|
125
|
+
end
|
103
126
|
File.open(File.join(dir, "#{component_name}.json"), "w") do |component_file|
|
104
|
-
#component_file.print(component_obj.to_json)
|
105
127
|
component_file.print(JSON.pretty_generate(component_obj))
|
106
128
|
end
|
107
129
|
end
|
108
130
|
end
|
109
131
|
|
132
|
+
def load_object(klass, name, try = 1)
|
133
|
+
klass.load(name)
|
134
|
+
rescue NoMethodError
|
135
|
+
ui.warn "Problem loading #{klass} #{name}. Try #{try}/#{LOAD_TRIES}"
|
136
|
+
if try < LOAD_TRIES
|
137
|
+
try += 1
|
138
|
+
load_object(klass, name, try)
|
139
|
+
end
|
140
|
+
rescue Net::HTTPServerException => e
|
141
|
+
if config[:ignore_perm]
|
142
|
+
ui.warn "Problem loading #{name} #{e.message}."
|
143
|
+
ui.warn "Possibly an issue with permissions on the chef server... Skipping"
|
144
|
+
else
|
145
|
+
raise
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
110
149
|
def cookbooks
|
111
150
|
ui.msg "Backing up cookbooks"
|
112
151
|
dir = File.join(config[:backup_dir], "cookbooks")
|
@@ -32,27 +32,36 @@ module ServerBackup
|
|
32
32
|
require 'chef/knife/core/object_loader'
|
33
33
|
require 'chef/cookbook_uploader'
|
34
34
|
require 'chef/api_client'
|
35
|
+
require 'securerandom'
|
36
|
+
require 'json'
|
35
37
|
end
|
36
38
|
|
37
39
|
banner "knife backup restore [COMPONENT [COMPONENT ...]] [-D DIR] (options)"
|
38
40
|
|
39
41
|
option :backup_dir,
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
:short => "-D DIR",
|
43
|
+
:long => "--backup-directory DIR",
|
44
|
+
:description => "Restore backup data from DIR.",
|
45
|
+
:default => Chef::Config[:knife][:chef_server_backup_dir] ? Chef::Config[:knife][:chef_server_backup_dir] : File.join(".chef", "chef_server_backup")
|
46
|
+
|
47
|
+
option :ignore_metadata_errors,
|
48
|
+
:short => "-I",
|
49
|
+
:long => "--ignore-metadata-errors",
|
50
|
+
:description => "Ignore json metadata errors when restoring cookbooks",
|
51
|
+
:boolean => true,
|
52
|
+
:default => Chef::Config[:knife][:ignore_metadata_errors] ? Chef::Config[:knife][:ignore_metadata_errors] : false
|
44
53
|
|
45
54
|
def run
|
46
55
|
ui.warn "This will overwrite existing data!"
|
47
56
|
ui.warn "Backup is at least 1 day old" if (Time.now - File.atime(config[:backup_dir])) > 86400
|
48
|
-
ui.confirm "Do you want to restore backup, possibly overwriting
|
57
|
+
ui.confirm "Do you want to restore backup, possibly overwriting existing data"
|
49
58
|
validate!
|
50
59
|
components = name_args.empty? ? COMPONENTS : name_args
|
51
60
|
Array(components).each { |component| self.send(component) }
|
52
61
|
end
|
53
62
|
|
54
63
|
private
|
55
|
-
COMPONENTS = %w(clients nodes roles data_bags environments cookbooks)
|
64
|
+
COMPONENTS = %w(clients users nodes roles data_bags environments cookbooks)
|
56
65
|
|
57
66
|
def validate!
|
58
67
|
bad_names = name_args - COMPONENTS
|
@@ -116,12 +125,14 @@ module ServerBackup
|
|
116
125
|
ui.info "=== Restoring clients ==="
|
117
126
|
clients = Dir.glob(File.join(config[:backup_dir], "clients", "*.json"))
|
118
127
|
clients.each do |file|
|
128
|
+
ui.info "Restoring clients from #{file}"
|
119
129
|
client = JSON.parse(IO.read(file))
|
120
130
|
begin
|
121
131
|
rest.post_rest("clients", {
|
122
132
|
:name => client['name'],
|
123
133
|
:public_key => client['public_key'],
|
124
|
-
:admin => client['admin']
|
134
|
+
:admin => client['admin'],
|
135
|
+
:validator => client['validator']
|
125
136
|
})
|
126
137
|
rescue Net::HTTPServerException => e
|
127
138
|
handle_error 'client', client['name'], e
|
@@ -129,32 +140,83 @@ module ServerBackup
|
|
129
140
|
end
|
130
141
|
end
|
131
142
|
|
143
|
+
def users
|
144
|
+
JSON.create_id = "no_thanks"
|
145
|
+
ui.info "=== Restoring users ==="
|
146
|
+
users = Dir.glob(File.join(config[:backup_dir], "users", "*.json"))
|
147
|
+
if !users.empty? and Chef::VERSION !~ /^11\./
|
148
|
+
ui.warn "users restore only supported on chef == 11"
|
149
|
+
return
|
150
|
+
end
|
151
|
+
users.each do |file|
|
152
|
+
user = JSON.parse(IO.read(file))
|
153
|
+
password = SecureRandom.hex[0..7]
|
154
|
+
begin
|
155
|
+
rest.post_rest("users", {
|
156
|
+
:name => user['name'],
|
157
|
+
:public_key => user['public_key'],
|
158
|
+
:admin => user['admin'],
|
159
|
+
:password => password
|
160
|
+
})
|
161
|
+
ui.info "Set password for #{user['name']} to #{password}, please update"
|
162
|
+
rescue Net::HTTPServerException => e
|
163
|
+
handle_error 'user', user['name'], e
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
132
168
|
def cookbooks
|
169
|
+
count = 0
|
133
170
|
ui.info "=== Restoring cookbooks ==="
|
134
171
|
cookbooks = Dir.glob(File.join(config[:backup_dir], "cookbooks", '*'))
|
172
|
+
#Make tmp dir
|
173
|
+
FileUtils.rm_rf(config[:backup_dir] + "/tmp")
|
174
|
+
Dir.mkdir config[:backup_dir] + "/tmp"
|
135
175
|
cookbooks.each do |cb|
|
136
|
-
full_cb =
|
137
|
-
|
138
|
-
|
139
|
-
|
176
|
+
full_cb = File.expand_path(cb)
|
177
|
+
cb_name = File.basename(cb)
|
178
|
+
cookbook = cb_name.reverse.split('-',2).last.reverse
|
179
|
+
full_path = File.join(config[:backup_dir] + "/tmp", cookbook)
|
140
180
|
begin
|
141
|
-
|
181
|
+
count += 1
|
182
|
+
if Chef::Platform.windows?
|
183
|
+
Dir.mkdir config[:backup_dir] + "/tmp/#{cb_name}"
|
184
|
+
full_path = File.join(config[:backup_dir] + "/tmp/#{cb_name}", cookbook)
|
185
|
+
ui.info "Copy cookbook #{full_cb} to #{full_path}"
|
186
|
+
FileUtils.copy_entry(full_cb, full_path)
|
187
|
+
else
|
188
|
+
full_path = File.join(config[:backup_dir] + "/tmp", cookbook)
|
189
|
+
File.symlink(full_cb, full_path)
|
190
|
+
end
|
142
191
|
cbu = Chef::Knife::CookbookUpload.new
|
143
192
|
Chef::Knife::CookbookUpload.load_deps
|
144
193
|
cbu.name_args = [ cookbook ]
|
145
|
-
cbu.config[:cookbook_path] = File.
|
194
|
+
cbu.config[:cookbook_path] = File.dirname(full_path)
|
146
195
|
ui.info "Restoring cookbook #{cbu.name_args}"
|
147
196
|
cbu.run
|
148
197
|
rescue Net::HTTPServerException => e
|
149
|
-
handle_error 'cookbook',
|
198
|
+
handle_error 'cookbook', cb_name, e
|
199
|
+
rescue Chef::Exceptions::JSON::ParseError => e
|
200
|
+
handle_error 'cookbook', cb_name, e
|
201
|
+
throw e unless config[:ignore_metadata_errors]
|
150
202
|
ensure
|
151
|
-
|
203
|
+
if Chef::Platform.windows?
|
204
|
+
rm_path = config[:backup_dir] + "/tmp/#{cb_name}"
|
205
|
+
ui.info "remove dir #{rm_path}"
|
206
|
+
FileUtils.remove_dir(rm_path, force = true)
|
207
|
+
else
|
208
|
+
File.unlink(full_path)
|
209
|
+
end
|
152
210
|
end
|
153
211
|
end
|
212
|
+
ui.info "Uploaded #{count} Cookbooks"
|
213
|
+
FileUtils.rm_rf(config[:backup_dir] + "/tmp")
|
154
214
|
end
|
155
215
|
|
156
216
|
def handle_error(type, name, error)
|
157
217
|
thing = "#{type}[#{name}]"
|
218
|
+
return ui.error "Error parsing JSON for: #{thing}" if error.kind_of?(Chef::Exceptions::JSON::ParseError)
|
219
|
+
|
158
220
|
case error.response
|
159
221
|
when Net::HTTPConflict # 409
|
160
222
|
ui.warn "#{thing} already exists; skipping"
|
data/lib/knife-backup/version.rb
CHANGED
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marius Ducea
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 0.10.10
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.10.10
|
27
27
|
description: Chef knife plugins to help backup and restore chef servers
|
@@ -31,7 +31,7 @@ executables: []
|
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
|
-
- .gitignore
|
34
|
+
- ".gitignore"
|
35
35
|
- Gemfile
|
36
36
|
- LICENSE
|
37
37
|
- README.md
|
@@ -42,7 +42,8 @@ files:
|
|
42
42
|
- lib/knife-backup.rb
|
43
43
|
- lib/knife-backup/version.rb
|
44
44
|
homepage: https://github.com/mdxp/knife-backup
|
45
|
-
licenses:
|
45
|
+
licenses:
|
46
|
+
- Apache 2.0
|
46
47
|
metadata: {}
|
47
48
|
post_install_message:
|
48
49
|
rdoc_options: []
|
@@ -50,18 +51,19 @@ require_paths:
|
|
50
51
|
- lib
|
51
52
|
required_ruby_version: !ruby/object:Gem::Requirement
|
52
53
|
requirements:
|
53
|
-
- -
|
54
|
+
- - ">="
|
54
55
|
- !ruby/object:Gem::Version
|
55
56
|
version: '0'
|
56
57
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
58
|
requirements:
|
58
|
-
- -
|
59
|
+
- - ">="
|
59
60
|
- !ruby/object:Gem::Version
|
60
61
|
version: '0'
|
61
62
|
requirements: []
|
62
63
|
rubyforge_project: knife-backup
|
63
|
-
rubygems_version: 2.
|
64
|
+
rubygems_version: 2.4.8
|
64
65
|
signing_key:
|
65
66
|
specification_version: 4
|
66
67
|
summary: Chef knife plugins to help backup and restore chef servers
|
67
68
|
test_files: []
|
69
|
+
has_rdoc:
|