knife-backup 0.0.2

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.
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: