knife-backup 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in knife-backup.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "knife-backup/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "knife-backup"
7
+ s.version = Knife::Backup::VERSION
8
+ s.authors = ["Marius Ducea"]
9
+ s.email = ["marius.ducea@gmail.com"]
10
+ s.homepage = "https://github.com/mdxp/knife-backup"
11
+ s.summary = %q{Chef knife plugins to help backup and restore chef servers}
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = "knife-backup"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "chef", ">= 0.10.10"
22
+
23
+ end
@@ -0,0 +1,130 @@
1
+ #
2
+ # Author:: Marius Ducea (<marius.ducea@gmail.com>)
3
+ # Author:: Steven Danna (steve@opscode.com)
4
+ # Author:: Joshua Timberman (<joshua@opscode.com>)
5
+ # Author:: Adam Jacob (<adam@opscode.com>)
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'chef/node'
21
+ require 'chef/api_client'
22
+ require 'chef/knife/cookbook_download'
23
+
24
+
25
+ module ServerBackup
26
+ class BackupExport < Chef::Knife
27
+
28
+ deps do
29
+ require 'fileutils'
30
+ require 'chef/cookbook_loader'
31
+ end
32
+
33
+ banner "knife backup export [-d DIR]"
34
+
35
+ option :backup_dir,
36
+ :short => "-d DIR",
37
+ :long => "--backup-directory DIR",
38
+ :description => "Store backup data in DIR. DIR will be created if it does not already exist.",
39
+ :default => Chef::Config[:knife][:chef_server_backup_dir] ? Chef::Config[:knife][:chef_server_backup_dir] : File.join(".chef", "chef_server_backup")
40
+
41
+ option :latest,
42
+ :short => "-N",
43
+ :long => "--latest",
44
+ :description => "The version of the cookbook to download",
45
+ :boolean => true
46
+
47
+ def run
48
+ clients
49
+ nodes
50
+ roles
51
+ data_bags
52
+ environments
53
+ cookbooks
54
+ end
55
+
56
+ def nodes
57
+ backup_standard("nodes", Chef::Node)
58
+ end
59
+
60
+ def clients
61
+ backup_standard("clients", Chef::ApiClient)
62
+ end
63
+
64
+ def roles
65
+ backup_standard("roles", Chef::Role)
66
+ end
67
+
68
+ def environments
69
+ backup_standard("environments", Chef::Environment)
70
+ end
71
+
72
+ def data_bags
73
+ ui.msg "Backing up data bags"
74
+ dir = File.join(config[:backup_dir], "data_bags")
75
+ FileUtils.mkdir_p(dir)
76
+ Chef::DataBag.list.each do |bag_name, url|
77
+ FileUtils.mkdir_p(File.join(dir, bag_name))
78
+ Chef::DataBag.load(bag_name).each do |item_name, url|
79
+ ui.msg "Backing up data bag #{bag_name} item #{item_name}"
80
+ item = Chef::DataBagItem.load(bag_name, item_name)
81
+ File.open(File.join(dir, bag_name, "#{item_name}.json"), "w") do |dbag_file|
82
+ dbag_file.print(JSON.pretty_generate(item.raw_data))
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def backup_standard(component, klass)
89
+ ui.msg "Backing up #{component}"
90
+ dir = File.join(config[:backup_dir], component)
91
+ FileUtils.mkdir_p(dir)
92
+ klass.list.each do |component_name, url|
93
+ next if component == "environments" && component_name == "_default"
94
+ ui.msg "Backing up #{component} #{component_name}"
95
+ component_obj = klass.load(component_name)
96
+ File.open(File.join(dir, "#{component_name}.json"), "w") do |component_file|
97
+ #component_file.print(component_obj.to_json)
98
+ component_file.print(JSON.pretty_generate(component_obj))
99
+ end
100
+ end
101
+ end
102
+
103
+ def cookbooks
104
+ ui.msg "Backing up cookboks"
105
+ dir = File.join(config[:backup_dir], "cookbooks")
106
+ FileUtils.mkdir_p(dir)
107
+ if config[:latest]
108
+ cookbooks = rest.get_rest("/cookbooks?latest")
109
+ else
110
+ cookbooks = rest.get_rest("/cookbooks?num_versions=all")
111
+ end
112
+ cookbooks.keys.each do |cb|
113
+ ui.msg "Backing up coookbook #{cb}"
114
+ dld = Chef::Knife::CookbookDownload.new
115
+ cookbooks[cb]['versions'].each do |ver|
116
+ dld.name_args = [cb, ver['version']]
117
+ dld.config[:download_directory] = dir
118
+ dld.config[:force] = true
119
+ begin
120
+ dld.run
121
+ rescue
122
+ ui.msg "Failed to download cookbook #{cb} version #{ver['version']}... Skipping"
123
+ FileUtils.rm_r(File.join(dir, cb + "-" + ver['version']))
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,138 @@
1
+ #
2
+ # Author:: Marius Ducea (<marius.ducea@gmail.com>)
3
+ # Author:: Steven Danna (steve@opscode.com)
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ class Chef
19
+ class Knife
20
+ class CookbookUpload
21
+ def check_for_dependencies!(cookbook)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+
28
+ module ServerBackup
29
+ class BackupRestore < Chef::Knife
30
+
31
+ deps do
32
+ require 'chef/knife/core/object_loader'
33
+ require 'chef/cookbook_uploader'
34
+ require 'chef/api_client'
35
+ end
36
+
37
+ banner "knife backup restore [-d DIR]"
38
+
39
+ option :backup_dir,
40
+ :short => "-d DIR",
41
+ :long => "--backup-directory DIR",
42
+ :description => "Restore backup data from DIR.",
43
+ :default => Chef::Config[:knife][:chef_server_backup_dir] ? Chef::Config[:knife][:chef_server_backup_dir] : File.join(".chef", "chef_server_backup")
44
+
45
+ def run
46
+ ui.warn "This will overwrite existing data!"
47
+ 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 exisitng data"
49
+ clients
50
+ nodes
51
+ roles
52
+ data_bags
53
+ environments
54
+ cookbooks
55
+ end
56
+
57
+ def nodes
58
+ restore_standard("nodes", Chef::Node)
59
+ end
60
+
61
+ def roles
62
+ restore_standard("roles", Chef::Role)
63
+ end
64
+
65
+ def environments
66
+ restore_standard("environments", Chef::Environment)
67
+ end
68
+
69
+ def data_bags
70
+ ui.msg "Restoring data bags"
71
+ loader = Chef::Knife::Core::ObjectLoader.new(Chef::DataBagItem, ui)
72
+ dbags = Dir.glob(File.join(config[:backup_dir], "data_bags", '*'))
73
+ dbags.each do |bag|
74
+ bag_name = File.basename(bag)
75
+ ui.msg "Creating data bag #{bag_name}"
76
+ rest.post_rest("data", { "name" => bag_name})
77
+ dbag_items = Dir.glob(File.join(bag, "*"))
78
+ dbag_items.each do |item_path|
79
+ item_name = File.basename(item_path, '.json')
80
+ ui.msg "Restoring data_bag_item[#{bag_name}::#{item_name}]"
81
+ item = loader.load_from("data_bags", bag_name, item_path)
82
+ dbag = Chef::DataBagItem.new
83
+ dbag.data_bag(bag_name)
84
+ dbag.raw_data = item
85
+ dbag.save
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ def restore_standard(component, klass)
92
+ loader = Chef::Knife::Core::ObjectLoader.new(klass, ui)
93
+ ui.msg "Restoring #{component}"
94
+ files = Dir.glob(File.join(config[:backup_dir], component, "*.json"))
95
+ files.each do |f|
96
+ ui.msg "Updating #{component} from #{f}"
97
+ updated = loader.load_from(component, f)
98
+ updated.save
99
+ end
100
+ end
101
+
102
+ def clients
103
+ JSON.create_id = "no_thanks"
104
+ ui.msg "Restoring clients"
105
+ clients = Dir.glob(File.join(config[:backup_dir], "clients", "*.json"))
106
+ clients.each do |file|
107
+ client = JSON.parse(IO.read(file))
108
+ begin
109
+ rest.post_rest("clients", {
110
+ :name => client['name'],
111
+ :public_key => client['public_key'],
112
+ :admin => client['admin']
113
+ })
114
+ rescue
115
+ ui.msg "#{client['name']} already exists; skipping"
116
+ end
117
+ end
118
+ end
119
+
120
+ def cookbooks
121
+ ui.msg "Restoring cookbooks"
122
+ cookbooks = Dir.glob(File.join(config[:backup_dir], "cookbooks", '*'))
123
+ cookbooks.each do |cb|
124
+ full_cb = cb.split("/").last
125
+ cookbook = full_cb.reverse.split('-',2).last.reverse
126
+ full_path = File.join(config[:backup_dir], "cookbooks", cookbook)
127
+ File.symlink(full_cb, full_path)
128
+ cbu = Chef::Knife::CookbookUpload.new
129
+ cbu.name_args = [ cookbook ]
130
+ cbu.config[:cookbook_path] = File.join(config[:backup_dir], "cookbooks")
131
+ puts cbu.name_args
132
+ cbu.run
133
+ File.unlink(full_path)
134
+ end
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1 @@
1
+ require "knife-backup/version"
@@ -0,0 +1,5 @@
1
+ module Knife
2
+ module Backup
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-backup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Marius Ducea
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chef
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.10.10
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.10.10
30
+ description: Chef knife plugins to help backup and restore chef servers
31
+ email:
32
+ - marius.ducea@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - Rakefile
40
+ - knife-backup.gemspec
41
+ - lib/chef/knife/backup_export.rb
42
+ - lib/chef/knife/backup_restore.rb
43
+ - lib/knife-backup.rb
44
+ - lib/knife-backup/version.rb
45
+ homepage: https://github.com/mdxp/knife-backup
46
+ licenses: []
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project: knife-backup
65
+ rubygems_version: 1.8.24
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Chef knife plugins to help backup and restore chef servers
69
+ test_files: []
70
+ has_rdoc: