chef_backup 0.0.1.dev.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.kitchen.yml +30 -0
  4. data/.rubocop.yml +17 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +4 -0
  7. data/Guardfile +22 -0
  8. data/LICENSE +13 -0
  9. data/README.md +33 -0
  10. data/Rakefile +44 -0
  11. data/chef_backup.gemspec +33 -0
  12. data/lib/chef_backup.rb +12 -0
  13. data/lib/chef_backup/config.rb +57 -0
  14. data/lib/chef_backup/data_map.rb +44 -0
  15. data/lib/chef_backup/exceptions.rb +12 -0
  16. data/lib/chef_backup/helpers.rb +159 -0
  17. data/lib/chef_backup/logger.rb +39 -0
  18. data/lib/chef_backup/runner.rb +136 -0
  19. data/lib/chef_backup/strategy.rb +29 -0
  20. data/lib/chef_backup/strategy/backup/custom.rb +7 -0
  21. data/lib/chef_backup/strategy/backup/ebs.rb +28 -0
  22. data/lib/chef_backup/strategy/backup/lvm.rb +42 -0
  23. data/lib/chef_backup/strategy/backup/object.rb +29 -0
  24. data/lib/chef_backup/strategy/backup/tar.rb +165 -0
  25. data/lib/chef_backup/strategy/restore/custom.rb +0 -0
  26. data/lib/chef_backup/strategy/restore/ebs.rb +0 -0
  27. data/lib/chef_backup/strategy/restore/lvm.rb +0 -0
  28. data/lib/chef_backup/strategy/restore/object.rb +0 -0
  29. data/lib/chef_backup/strategy/restore/tar.rb +125 -0
  30. data/lib/chef_backup/version.rb +4 -0
  31. data/spec/fixtures/chef-server-running.json +584 -0
  32. data/spec/spec_helper.rb +103 -0
  33. data/spec/unit/data_map_spec.rb +59 -0
  34. data/spec/unit/helpers_spec.rb +88 -0
  35. data/spec/unit/runner_spec.rb +185 -0
  36. data/spec/unit/shared_examples/helpers.rb +20 -0
  37. data/spec/unit/strategy/backup/lvm_spec.rb +0 -0
  38. data/spec/unit/strategy/backup/shared_examples/backup.rb +74 -0
  39. data/spec/unit/strategy/backup/tar_spec.rb +280 -0
  40. data/spec/unit/strategy/restore/lvm_spec.rb +0 -0
  41. data/spec/unit/strategy/restore/shared_examples/restore.rb +84 -0
  42. data/spec/unit/strategy/restore/tar_spec.rb +238 -0
  43. data/spec/unit/strategy_spec.rb +36 -0
  44. metadata +253 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1bec5855a4a67f6d47efbf80b1b13be09338de3d
4
+ data.tar.gz: c02131b03380815d3a79c281da26fb8fe51f7f7f
5
+ SHA512:
6
+ metadata.gz: 34616288ba8212f0e86fb3e5b26ad75bd6a912c9de52123ea954179c1a52ced09ed5f4f00368bf2c17b4ddb32642ba0f78e76a0da0071510bf8851bc0af93a1f
7
+ data.tar.gz: 02e5d4f0bc8868228c9f329702adfe8099c7f866e7f2e82760a65cd153a487c861e950184eddc938e578db894eb41ac6fcb6a84e7498641a7cbc7a0bb0e26f32
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ *.sw*
@@ -0,0 +1,30 @@
1
+ driver:
2
+ name: vagrant
3
+ forward_agent: true
4
+ customize:
5
+ cpus: 2
6
+ memory: 2048
7
+ synced_folders:
8
+ - ['.', '/home/vagrant/chef_backup']
9
+ - ['../../opscode/chef_packages/', '/home/vagrant/chef_packages']
10
+
11
+ provisioner:
12
+ name: chef_zero
13
+ require_chef_omnibus: 11.16.4
14
+
15
+ platforms:
16
+ - name: ubuntu-14.04
17
+ run_list: apt::default
18
+ - name: ubuntu-12.04
19
+ run_list: apt::default
20
+ - name: ubuntu-11.04
21
+ run_list: apt::default
22
+ - name: ubuntu-10.04
23
+ run_list: apt::default
24
+ - name: centos-5.10
25
+ - name: centos-6.5
26
+
27
+ suites:
28
+ - name: default
29
+ attributes:
30
+ run_list:
@@ -0,0 +1,17 @@
1
+ Style/ClassAndModuleChildren:
2
+ Enabled: false
3
+ Style/GuardClause:
4
+ Enabled: false
5
+ Metrics/ClassLength:
6
+ Max: 200
7
+ Metrics/CyclomaticComplexity:
8
+ Max: 10
9
+ Metrics/PerceivedComplexity:
10
+ Max: 10
11
+ Metrics/MethodLength:
12
+ Max: 40
13
+ Metrics/AbcSize:
14
+ Max: 25
15
+ AllCops:
16
+ Exclude:
17
+ - 'Guardfile'
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.1.5"
4
+ - "2.2.0"
5
+ notifications:
6
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ec_backup.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ # Note: The cmd option is now required due to the increasing number of ways
5
+ # rspec may be run, below are examples of the most common uses.
6
+ # * bundler: 'bundle exec rspec'
7
+ # * bundler binstubs: 'bin/rspec'
8
+ # * spring: 'bin/rsspec' (This will use spring if running and you have
9
+ # installed the spring binstubs per the docs)
10
+ # * zeus: 'zeus rspec' (requires the server to be started separetly)
11
+ # * 'just' rspec: 'rspec'
12
+ guard :rspec, cmd: 'bundle exec rspec -f doc --color', all_after_pass: true do
13
+ watch(%r{^spec/.+/.+_spec\.rb$})
14
+ watch(%r{^spec/.+/.+/.+_spec\.rb$})
15
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
16
+ watch(%r{^lib/chef_backup/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
17
+ watch(%r{^lib/chef_backup/(.+)/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}/#{m[2]}_spec.rb" }
18
+ watch(%r{^lib/chef_backup/(.+)/(.+)/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}/#{m[2]}/#{m[3]}_spec.rb" }
19
+ watch('spec/spec_helper.rb') { "spec" }
20
+ end
21
+
22
+ notification :tmux
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2014 Chef Software, Inc
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,33 @@
1
+ # ChefBackup
2
+ [![Build Status](https://travis-ci.org/ryancragun/chef_backup.svg?branch=master)](https://travis-ci.org/ryancragun/chef_backup)
3
+
4
+ A gem that backs up and restores Chef servers.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'chef_backup'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install chef_backup
19
+
20
+ ## Usage
21
+
22
+ ```shell
23
+ chef-server-ctl backup
24
+ chef-server-ctl restore some_backup.tgz
25
+ ```
26
+
27
+ ## Contributing
28
+
29
+ 1. Fork it ( https://github.com/ryancragun/chef_backup/fork )
30
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
31
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
32
+ 4. Push to the branch (`git push origin my-new-feature`)
33
+ 5. Create a new Pull Request
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'rake'
4
+ require 'rspec'
5
+ require 'rspec/core'
6
+ require 'rspec/core/rake_task'
7
+ require 'bundler'
8
+ require 'bundler/gem_tasks'
9
+ require 'rubocop/rake_task'
10
+
11
+ desc 'Default task to run spec suite'
12
+ task default: %w(spec rubocop)
13
+
14
+ desc 'Run spec suite'
15
+ RSpec::Core::RakeTask.new(:spec) do |task|
16
+ task.pattern = FileList['spec/**/*_spec.rb']
17
+ end
18
+
19
+ desc 'Run RSpec with code coverage'
20
+ task :coverage do
21
+ ENV['COVERAGE'] = 'true'
22
+ Rake::Task['spec'].execute
23
+ end
24
+
25
+ desc 'Run Rubocop style checks'
26
+ RuboCop::RakeTask.new do |cop|
27
+ cop.fail_on_error = true
28
+ end
29
+
30
+ desc 'console'
31
+ task :console do
32
+ require 'pry'
33
+ require 'chef_backup'
34
+ require 'json'
35
+ f = File.expand_path('../spec/fixtures/chef-server-running.json', __FILE__)
36
+ running_config = JSON.parse(File.read(f))
37
+ @runner = ChefBackup::Runner.new(
38
+ running_config.merge('restore_param' => '/tmp/backup.tgz')
39
+ )
40
+ ARGV.clear
41
+ Pry.config.history.should_save = true
42
+ Pry.config.history.should_load = true
43
+ Pry.start
44
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chef_backup/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'chef_backup'
8
+ spec.version = ChefBackup::VERSION
9
+ spec.authors = ['Ryan Cragun']
10
+ spec.email = ['me@ryan.ec']
11
+ spec.summary = 'A library to backup an Enterprise Chef server'
12
+ spec.description = spec.summary
13
+ spec.homepage = 'https://github.com/ryancragun/chef_backup'
14
+ spec.license = 'Apachev2'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(/^(test|spec|features)/)
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'di-ruby-lvm'
22
+ spec.add_dependency 'mixlib-shellout'
23
+ spec.add_dependency 'highline'
24
+ spec.add_dependency 'chef'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.6'
27
+ spec.add_development_dependency 'rake'
28
+ spec.add_development_dependency 'rspec', '~> 3.0'
29
+ spec.add_development_dependency 'guard-rspec'
30
+ spec.add_development_dependency 'pry-rescue'
31
+ spec.add_development_dependency 'rubocop', '0.28'
32
+ spec.add_development_dependency 'simplecov'
33
+ end
@@ -0,0 +1,12 @@
1
+ # Copyright:: Copyright (c) 2014 Chef, Inc.
2
+ #
3
+ # All Rights Reserved
4
+
5
+ require 'chef_backup/version'
6
+ require 'chef_backup/exceptions'
7
+ require 'chef_backup/config'
8
+ require 'chef_backup/logger'
9
+ require 'chef_backup/data_map'
10
+ require 'chef_backup/helpers'
11
+ require 'chef_backup/runner'
12
+ require 'chef_backup/strategy'
@@ -0,0 +1,57 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+ require 'forwardable'
4
+
5
+ module ChefBackup
6
+ # ChefBackup Global Config
7
+ class Config
8
+ extend Forwardable
9
+
10
+ DEFAULT_CONFIG = {
11
+ 'backup' => {
12
+ 'always_dump_db' => true,
13
+ 'strategy' => 'none',
14
+ 'export_dir' => '/var/opt/chef-backup'
15
+ }
16
+ }.freeze
17
+
18
+ class << self
19
+ def config
20
+ @config ||= new
21
+ end
22
+
23
+ def config=(hash)
24
+ @config = new(hash)
25
+ end
26
+
27
+ def [](key)
28
+ config[key]
29
+ end
30
+
31
+ def []=(key, value)
32
+ config[key] = value
33
+ end
34
+
35
+ #
36
+ # @param file [String] path to a JSON configration file
37
+ #
38
+ def from_json_file(file)
39
+ path = File.expand_path(file)
40
+ @config = new(JSON.parse(File.read(path))) if File.exist?(path)
41
+ end
42
+ end
43
+
44
+ #
45
+ # @param config [Hash] a Hash of the private-chef-running.json
46
+ #
47
+ def initialize(config = {})
48
+ config['private_chef'] ||= {}
49
+ config['private_chef']['backup'] ||= {}
50
+ config['private_chef']['backup'] =
51
+ DEFAULT_CONFIG['backup'].merge(config['private_chef']['backup'])
52
+ @config = config
53
+ end
54
+
55
+ def_delegators :@config, :[], :[]=
56
+ end
57
+ end
@@ -0,0 +1,44 @@
1
+ require 'time'
2
+
3
+ module ChefBackup
4
+ # DataMap class to store data about the data we're backing up
5
+ class DataMap
6
+ class << self
7
+ def data_map
8
+ @data_map ||= new
9
+ end
10
+
11
+ attr_writer :data_map
12
+ end
13
+
14
+ attr_accessor :strategy, :backup_time, :configs, :services
15
+
16
+ def initialize
17
+ @services = {}
18
+ @configs = {}
19
+ yield self if block_given?
20
+
21
+ @backup_time ||= Time.now.iso8601
22
+ @strategy ||= 'none'
23
+ end
24
+
25
+ def add_service(service, data_dir)
26
+ @services[service] ||= {}
27
+ @services[service]['data_dir'] = data_dir
28
+ end
29
+
30
+ def add_config(config, path)
31
+ @configs[config] ||= {}
32
+ @configs[config]['data_dir'] = path
33
+ end
34
+
35
+ def manifest
36
+ {
37
+ 'strategy' => strategy,
38
+ 'backup_time' => backup_time,
39
+ 'services' => services,
40
+ 'configs' => configs
41
+ }
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ module ChefBackup
2
+ module Exceptions
3
+ # ChefBackup Exceptions
4
+ class ChefBackupException < StandardError; end
5
+ class InvalidTarball < ChefBackupException; end
6
+ class InvalidSnapshot < ChefBackupException; end
7
+ class InvalidDatabaseDump < ChefBackupException; end
8
+ class InvalidManifest < ChefBackupException; end
9
+ class InvalidStrategy < ChefBackupException; end
10
+ class NotImplementedError < ChefBackupException; end
11
+ end
12
+ end
@@ -0,0 +1,159 @@
1
+ require 'fileutils'
2
+ require 'mixlib/shellout'
3
+ require 'chef_backup/config'
4
+ require 'chef_backup/logger'
5
+
6
+ # rubocop:disable IndentationWidth
7
+ module ChefBackup
8
+ # Common helper methods that are usefull in many classes
9
+ module Helpers
10
+ # rubocop:enable IndentationWidth
11
+
12
+ SERVER_ADD_ONS = %w(
13
+ opscode-manage
14
+ opscode-reporting
15
+ opscode-push-jobs-server
16
+ opscode-analytics
17
+ chef-ha
18
+ chef-sync
19
+ ).freeze
20
+
21
+ def private_chef
22
+ config['private_chef']
23
+ end
24
+
25
+ def config
26
+ ChefBackup::Config
27
+ end
28
+
29
+ def log(message, level = :info)
30
+ ChefBackup::Logger.logger.log(message, level)
31
+ end
32
+
33
+ #
34
+ # @param file [String] A path to a file on disk
35
+ # @param exception [Exception] An exception to raise if file is not present
36
+ # @param message [String] Exception message to raise
37
+ #
38
+ # @return [TrueClass, FalseClass]
39
+ #
40
+ def ensure_file!(file, exception, message)
41
+ File.exist?(file) ? true : fail(exception, message)
42
+ end
43
+
44
+ def shell_out(*command)
45
+ cmd = Mixlib::ShellOut.new(*command)
46
+ cmd.live_stream ||= $stdout.tty? ? $stdout : nil
47
+ cmd.run_command
48
+ cmd
49
+ end
50
+
51
+ def shell_out!(*command)
52
+ cmd = shell_out(*command)
53
+ cmd.error!
54
+ cmd
55
+ end
56
+
57
+ def all_services
58
+ Dir['/opt/opscode/sv/*'].map { |f| File.basename(f) }.sort
59
+ end
60
+
61
+ def enabled_services
62
+ all_services.select { |sv| service_enabled?(sv) }
63
+ end
64
+
65
+ def disabled_services
66
+ all_services.select { |sv| !service_enabled?(sv) }
67
+ end
68
+
69
+ def service_enabled?(service)
70
+ File.symlink?("/opt/opscode/service/#{service}")
71
+ end
72
+
73
+ def stop_service(service)
74
+ res = shell_out("chef-server-ctl stop #{service}")
75
+ res
76
+ end
77
+
78
+ def start_service(service)
79
+ res = shell_out("chef-server-ctl start #{service}")
80
+ res
81
+ end
82
+
83
+ def stop_chef_server(params = {})
84
+ log 'Bringing down the Chef Server'
85
+ services = enabled_services
86
+ services -= params[:except].map(&:to_s) if params.key?(:except)
87
+ services.each { |sv| stop_service(sv) }
88
+ end
89
+
90
+ def start_chef_server
91
+ log 'Bringing up the Chef Server'
92
+ enabled_services.each { |sv| start_service(sv) }
93
+ end
94
+
95
+ def enabled_addons
96
+ SERVER_ADD_ONS.select { |service| addon?(service) }
97
+ end
98
+
99
+ def addon?(service)
100
+ File.directory?("/etc/#{service}")
101
+ end
102
+
103
+ def pg_dump?
104
+ if frontend? # don't dump postgres on frontends
105
+ false
106
+ elsif private_chef['backup']['always_dump_db'] == true # defaults to true
107
+ true
108
+ elsif strategy !~ /lvm|ebs/ && backend? # backup non-block device backends
109
+ true
110
+ else
111
+ false # if we made it here then we're on lvm/ebs and overrode defaults
112
+ end
113
+ end
114
+
115
+ def strategy
116
+ private_chef['backup']['strategy']
117
+ end
118
+
119
+ def frontend?
120
+ private_chef['role'] == 'frontend'
121
+ end
122
+
123
+ def backend?
124
+ private_chef['role'] =~ /backend|standalone/
125
+ end
126
+
127
+ def online?
128
+ private_chef['backup']['mode'] == 'online'
129
+ end
130
+
131
+ def tmp_dir
132
+ @tmp_dir ||= begin
133
+ dir = safe_key { config['tmp_dir'] } ||
134
+ safe_key { private_chef['backup']['tmp_dir'] }
135
+ if dir
136
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
137
+ dir
138
+ else
139
+ Dir.mktmpdir('chef_backup')
140
+ end
141
+ end
142
+ end
143
+
144
+ def cleanup
145
+ log "Cleaning up #{tmp_dir}"
146
+ FileUtils.rm_r(tmp_dir)
147
+ rescue Errno::ENOENT
148
+ true
149
+ end
150
+
151
+ private
152
+
153
+ def safe_key
154
+ yield
155
+ rescue NameError
156
+ nil
157
+ end
158
+ end
159
+ end