landscape-turner 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in landscape_turner.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 DAQRI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Landscape Turner
2
+
3
+ Landscape Turner is a set of two ruby scripts for backing up the Landscape service, `landscape-turner-backup` and `landscape-turner-restore` .
4
+
5
+ ## General Usage
6
+
7
+ ```bash
8
+ $ landscape-turner-backup --snapshot-path=/home/daqri/backup
9
+
10
+ $ landscape-turner-restore /home/daqri/backup.tar.gz
11
+ ```
12
+
13
+ ## Options for landscape-turner-backup
14
+
15
+ ###-s, --snapshot-path=/path/to/snapshot
16
+ Required
17
+
18
+ ###-o, --override=*string*
19
+ Override paths with -o name1=path1
20
+
21
+ ### -d, --disable=*string*
22
+ Disable paths with -d name
23
+
24
+ ### -n, --no-db
25
+ Disable database backup
26
+
27
+ ### -l, --landscape-prefix=/path/to/default/dirs
28
+ Path to prefix default landscape dirs (/var, /etc), default is nothing.
29
+
30
+ ### -p, --no-op
31
+ No-op (dry run)
32
+
33
+
34
+ To override specific paths, use `-o name1=path1 -o name2=path2 -o name3=path3`. To disable specific paths for backup, use `-d name1 -d name2`. Overrides have higher priority than `--landscape-prefix`.
35
+
36
+ ## Options for landscape-turner-restore
37
+
38
+ ### -r, --restore-prefix=*string*
39
+ Prefix to prepend default landscape dirs (/var, /etc) with (default: nothing)
40
+
41
+ ### -n, --no-db
42
+ Disable database restore
43
+
44
+
45
+ In addition to the optional arguments, you must specify exactly one .tar.gz file to restore.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.test_files = FileList['test/test*.rb']
6
+ t.verbose= true
7
+ end
8
+
9
+
10
+
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "landscape_turner"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'landscape-turner/backup'
4
+
5
+ if ENV['USER'] != "root"
6
+ puts "You must have root permissions to run this tool."
7
+ exit -1
8
+ end
9
+
10
+ Backup.backup_with_cli_args
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'landscape-turner/restore'
4
+
5
+ if ENV['USER'] != "root"
6
+ puts "You must have root permissions to run this tool."
7
+ exit
8
+ end
9
+
10
+ Restore.restore_with_cli_args
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'landscape-turner/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "landscape-turner"
8
+ spec.version = LandscapeTurner::VERSION
9
+ spec.authors = ["Justin Boisvert"]
10
+ spec.email = ["justin.boisvert@daqri.com"]
11
+
12
+ spec.summary = "Backup tool for the Landscape service"
13
+ spec.description = "A backup tool for the Landscape service that backs up the configuration files and the database related to the service."
14
+ spec.homepage = "http://www.daqri.com"
15
+
16
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
17
+ # delete this section to allow pushing this gem to any host.
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ spec.bindir = "exe"
21
+ spec.executables << "landscape-turner-backup"
22
+ spec.executables << "landscape-turner-restore"
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_runtime_dependency "trollop"
26
+ spec.add_development_dependency "bundler", "~> 1.11"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest"
29
+ end
@@ -0,0 +1,116 @@
1
+ require_relative 'helpers'
2
+ require 'fileutils'
3
+ require 'pathname'
4
+ require 'open3'
5
+ require 'trollop'
6
+
7
+ class Backup
8
+ extend LandscapeTurner::Helpers
9
+
10
+ def self.backup_with_cli_args(args = ARGV)
11
+ paths = {
12
+ :x509_certificate => "/etc/ssl/certs/landscape_server.pem",
13
+ :ca_certificate => "/etc/ssl/certs/landscape_server_ca.crt",
14
+ :ssl_private_key => "/etc/ssl/private/landscape_server.key",
15
+ :postgresql_config => "/etc/postgresql/9.3/main",
16
+ :apache_config => "/etc/apache2/sites-available/landscape-server.conf",
17
+ :hash_id_database => "/var/lib/landscape/hash-id-databases",
18
+ :landscape_dir => "/etc/landscape",
19
+ :landscape_default => "/etc/default/landscape-server",
20
+ }
21
+
22
+ new_args = Trollop.options(args) do
23
+ opt :snapshot_path, "Path to snapshot (required)", :type => String
24
+ opt :override, "Override paths with -o name1=path1", :type => :strings
25
+ opt :disable, "Disable paths with -d name", :type => :strings
26
+ opt :no_db, "Disable database backup"
27
+ opt :landscape_prefix, "Path to prefix default landscape dirs (/var, /etc) with", :default => ""
28
+ opt :no_op, "No-op (dry run)"
29
+ opt :sudo, "Program to use as sudo", :default => "sudo"
30
+
31
+ banner <<-USAGE
32
+ To override specific paths, use -o name1=path1 -o name2=path2 -o name3=path3
33
+ To disable specific paths for backup, use -d name1 -d name2
34
+ Overrides have higher priority than --landscape-prefix.
35
+
36
+ Names with default values:
37
+ #{paths.keys.map { |k| " #{k}: #{paths[k]}"} }
38
+ USAGE
39
+ end
40
+
41
+ @@sudo = new_args[:sudo]
42
+
43
+ prefix = new_args[:landscape_prefix]
44
+ paths.each { |k, v| paths[k] = "#{prefix}#{v}" }
45
+
46
+ (new_args[:override] || []).each do |o|
47
+ key, val = o.split("=")
48
+ key = key.to_sym
49
+ raise "Unrecognized name argument: #{key.inspect}!" unless paths[key]
50
+ paths[key] = val
51
+ end
52
+
53
+ (new_args[:disable] || []).each do |d|
54
+ raise "Unrecognized name argument: #{d.inspect}!" unless paths[key]
55
+ paths.delete d.to_sym
56
+ end
57
+
58
+ backup_landscape(new_args, paths, prefix)
59
+ end
60
+
61
+ def self.backup_landscape(params = {}, paths, prefix)
62
+ #check required arguments needed
63
+ unless params[:snapshot_path]
64
+ raise ArgumentError, "Error: Must supply snapshot path with --snapshot-path"
65
+ end
66
+ destination = params[:snapshot_path] + "/"
67
+ operate = !params[:no_op]
68
+
69
+ #create the top-level destination path
70
+ operate ? (FileUtils.mkdir(destination) rescue nil) : nil
71
+
72
+ paths.each do |name, path|
73
+ # Get path relative to landscape_prefix
74
+ rel_path = Pathname.new(path).relative_path_from(Pathname.new(File.expand_path(prefix))).to_s
75
+
76
+ # Make sure destination subdir exists
77
+ dest_subdir = File.dirname "#{destination}/#{rel_path}"
78
+ puts "Copying #{path} to #{dest_subdir}..."
79
+ FileUtils.mkdir_p dest_subdir if operate
80
+ FileUtils.cp_r(path, dest_subdir, :preserve => true) if operate
81
+ end
82
+
83
+ puts "Backing up Postgres databases.."
84
+ (operate && !params[:no_db]) ? dump_landscape_databases(destination) : nil
85
+ destination = params[:snapshot_path]
86
+ puts "Compressing to file #{destination}.tar.gz"
87
+ tgz_file = "#{File.basename(destination)}.tar.gz"
88
+ operate ? safe_system("cd #{destination} && rm -f #{tgz_file} && tar -cpzf #{tgz_file} * && mv #{tgz_file} ..") : nil
89
+ if operate && !check_consistent_tar(destination)
90
+ raise "Tarball inconsistent with backup"
91
+ end
92
+ # remove the folder where we stored everything
93
+ operate ? FileUtils.remove_dir(destination) : nil
94
+ puts "Finished backing up to #{destination}"
95
+ end
96
+
97
+ def self.dump_landscape_databases(backup_destination)
98
+ FileUtils.mkdir("#{backup_destination}/postgresql_backup") rescue nil
99
+ get_landscape_databases().each { |database_name|
100
+ # pg_dump dbname > outfile
101
+ safe_system("cd /tmp && sudo -u postgres pg_dump #{database_name} > #{database_name}.bak")
102
+ FileUtils.mv("/tmp/#{database_name}.bak","#{backup_destination}/postgresql_backup/")
103
+ }
104
+ end
105
+
106
+ def self.check_consistent_tar(destination)
107
+ FileUtils.rm_rf("/tmp/tar_check")
108
+ puts "cd #{File.dirname(destination)} && mkdir /tmp/tar_check && tar -xf #{File.basename(destination)}.tar.gz -C /tmp/tar_check"
109
+ safe_system("cd #{File.dirname(destination)} && mkdir /tmp/tar_check && tar -xf #{File.basename(destination)}.tar.gz -C /tmp/tar_check")
110
+ hash_folder1 = Open3.capture3("find #{destination} -type f 2>/dev/null -exec md5sum {} \;")
111
+ hash_folder2 = Open3.capture3("find /tmp/tar_check -type f 2>/dev/null -exec md5sum {} \;")
112
+ FileUtils.rm_rf("/tmp/tar_check")
113
+ return hash_folder1 == hash_folder2
114
+ end
115
+
116
+ end
@@ -0,0 +1,25 @@
1
+ require 'open3'
2
+
3
+ module LandscapeTurner
4
+ module Helpers
5
+ def safe_system(cmd)
6
+ e = system(cmd)
7
+ raise "Failure in command: #{cmd.inspect}!" unless e
8
+ end
9
+ def get_landscape_databases()
10
+ landscape_databases = Array.new()
11
+ list = Open3.capture3("cd /tmp && sudo -u postgres psql -qlA")[0].split("\n")
12
+ #This is to remove irrelevant information generated from this command at the first and last line.
13
+ list.shift()
14
+ list.pop()
15
+ landscape_list = Array.new()
16
+ list.each { |item|
17
+ db_name = item.split("|")[0]
18
+ if db_name.start_with?("landscape")
19
+ landscape_list.push(db_name)
20
+ end
21
+ }
22
+ return landscape_list
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,87 @@
1
+ require_relative 'helpers'
2
+ require_relative 'backup'
3
+ require 'fileutils'
4
+
5
+ # TODO: Document that overridden files get restored to their
6
+ # original location
7
+
8
+ class Restore
9
+ extend LandscapeTurner::Helpers
10
+ POSTGRES_CONFIG = "/etc/postgresql/9.3/main"
11
+ EXCLUDE_FILES = [POSTGRES_CONFIG]
12
+ def self.restore_with_cli_args(args = ARGV)
13
+ opts = Trollop.options(args) do
14
+ opt :restore_prefix, "Prefix to prepend default landscape dirs (/var, /etc) with", :default => ""
15
+ opt :no_db, "Disable database restore"
16
+ opt :sudo, "Program to use as 'sudo'", :default => "sudo"
17
+
18
+ banner <<-USAGE
19
+ In addition to optional arguments, give exactly one .tar.gz file to restore.
20
+ USAGE
21
+ end
22
+
23
+ raise "You must supply exactly one argument, the backup path!" unless args.size == 1
24
+ @@sudo = opts[:sudo]
25
+ restore_landscape args[0], opts
26
+ end
27
+
28
+ def self.with_service_stopped(service)
29
+ begin
30
+ safe_system("#{@@sudo} service #{service} stop")
31
+ yield
32
+ ensure
33
+ safe_system("#{@@sudo} service #{service} start")
34
+ end
35
+ end
36
+
37
+ def self.restore_landscape(backup_loc, options = {})
38
+
39
+ with_service_stopped("landscape-server") do
40
+ expanded_path = File.expand_path(backup_loc)
41
+ FileUtils.rm_rf "/tmp/landscape_backup"
42
+ FileUtils.mkdir "/tmp/landscape_backup"
43
+ safe_system "cd /tmp/landscape_backup && tar xpf \"#{expanded_path}\""
44
+
45
+ Dir["/tmp/landscape_backup/*"].each do |top_level|
46
+ sub_path = top_level.sub /^\/tmp\/landscape_backup/, ""
47
+ next if EXCLUDE_FILES.any? { |exc| sub_path[exc] }
48
+ new_location = File.join options[:restore_prefix], sub_path
49
+ replace new_location, top_level
50
+ end
51
+
52
+ puts "Restoring Postgres"
53
+ unless options[:no_db]
54
+ restore_landscape_databases("/tmp/landscape_backup/postgresql_backup")
55
+ with_service_stopped("postgresql") do
56
+ replace(POSTGRES_CONFIG,"/tmp/landscape_backup/#{POSTGRES_CONFIG}")
57
+ end
58
+ end
59
+
60
+ puts "Restoration of Landscape configuration completed."
61
+
62
+ FileUtils.rm_rf "/tmp/landscape_backup" rescue nil
63
+ end
64
+
65
+ end
66
+
67
+ def self.replace(filename_replace,filename)
68
+ puts "Copying #{filename} to #{File.dirname(filename_replace)}..."
69
+ FileUtils.cp_r(filename, File.dirname(filename_replace), :preserve => true, :remove_destination => true)
70
+ end
71
+
72
+
73
+ def self.restore_landscape_databases(restore_location)
74
+ #clear the databases first
75
+ Dir["#{restore_location}/*"].each { |database_name|
76
+ database_name = File.basename(database_name)
77
+ database_name.slice!(".bak")
78
+ safe_system("cd /tmp && sudo -u postgres dropdb #{database_name}") rescue nil
79
+ safe_system("cd /tmp && sudo -u postgres createdb #{database_name}")
80
+ # psql dbname < infile
81
+ safe_system("cd /tmp && sudo -u postgres psql #{database_name} < #{restore_location}/#{database_name}.bak")
82
+ }
83
+
84
+ end
85
+
86
+
87
+ end
@@ -0,0 +1,3 @@
1
+ module LandscapeTurner
2
+ VERSION = "1.2.0"
3
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: landscape-turner
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Justin Boisvert
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2016-02-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: trollop
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
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'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.11'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.11'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '10.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: minitest
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: A backup tool for the Landscape service that backs up the configuration
79
+ files and the database related to the service.
80
+ email:
81
+ - justin.boisvert@daqri.com
82
+ executables:
83
+ - landscape-turner-backup
84
+ - landscape-turner-restore
85
+ extensions: []
86
+ extra_rdoc_files: []
87
+ files:
88
+ - Gemfile
89
+ - LICENSE.txt
90
+ - README.md
91
+ - Rakefile
92
+ - bin/console
93
+ - bin/setup
94
+ - exe/landscape-turner-backup
95
+ - exe/landscape-turner-restore
96
+ - landscape-turner.gemspec
97
+ - lib/landscape-turner/backup.rb
98
+ - lib/landscape-turner/helpers.rb
99
+ - lib/landscape-turner/restore.rb
100
+ - lib/landscape-turner/version.rb
101
+ homepage: http://www.daqri.com
102
+ licenses: []
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 1.8.23
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: Backup tool for the Landscape service
125
+ test_files: []