postgres-clone 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6aa1bdbfbc49b007f45a70183cfcf64f67f74d59
4
+ data.tar.gz: 6b42f1372dec85cb75629a7dae0ff2991e71baf6
5
+ SHA512:
6
+ metadata.gz: 2bbf06fd4cf8f2b4ca18f443d434b3bbf5d8da59d78f228a83269abacf04c6131b41af26e4cba9a5a6f4a3b86bc6e84dc538c89ef8c36082637a5d84e0bf0622
7
+ data.tar.gz: 9a246fda241024a02fcea58c080389057d89a0da1f33ce7bc3a1281e2a6a4bb06bec112405f523b98284d7f7bb94ec19ab110f4d7a3704685b1dfe6d23e850fd
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --order random
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.4
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in postgres-clone.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Josh Rickard
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,73 @@
1
+ [![Build Status](https://travis-ci.org/joshrickard/postgres-clone.svg?branch=master)](https://travis-ci.org/joshrickard/postgres-clone)
2
+
3
+ # PostgresClone
4
+
5
+ A command line utility for cloning Postgres databases.
6
+
7
+
8
+ ## Installation
9
+
10
+ ```ruby
11
+ gem install postgres-clone
12
+ ```
13
+
14
+
15
+ ## Usage
16
+
17
+ After installing the `postgres-clone` gem, you will have access to the `pg-clone` command which facilitates copying Postgres databases.
18
+
19
+ `pg-clone` will ssh into the remote server, perform a `pg_dump`, copy the dump to the target server, and then perform a `pg_restore`.
20
+
21
+
22
+ ### Options
23
+
24
+ TODO:
25
+
26
+
27
+ ### Examples
28
+
29
+ **Remote to Local**
30
+
31
+ ```
32
+ # Dump remote database and restore to local Postgres server
33
+ pg-clone --src-host=db.example.com --src-db=database --dst-host=localhost
34
+ ```
35
+
36
+ **Remote to Remote**
37
+
38
+ ```
39
+ # Dump remote database and restore to a different remote Postgres server
40
+ pg-clone --src-host=db1.example.com --src-db=database --dst-host=db2.example.com
41
+ ```
42
+
43
+ **Local to Local**
44
+
45
+ ```
46
+ # Duplicate local Postgres database
47
+ pg-clone --src-host=local --src-db=database --dst-host=localhost
48
+ ```
49
+
50
+ **Local to Remote**
51
+
52
+ ```
53
+ # Dump local database and restore to a remote database server
54
+ pg-clone --src-host=local --src-db=database --dst-host=db.example.com
55
+ ```
56
+
57
+
58
+ ## TODOS
59
+
60
+ * Test keyless runs
61
+ * Clean up database dumps?
62
+ * Add checks for existing databases
63
+ * Add options for all prompts
64
+ * Check for existing dump on target machines before doing work
65
+
66
+ ## Contributing
67
+
68
+ Bug reports and pull requests are welcome on GitHub at https://github.com/joshrickard/postgres-clone.
69
+
70
+
71
+ ## License
72
+
73
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'postgres-clone'
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
@@ -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,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'postgres-clone'
5
+
6
+ Postgres::Clone::Executable.start(ARGV)
@@ -0,0 +1,6 @@
1
+ require 'postgres/clone/version'
2
+ require 'postgres/clone/executable'
3
+
4
+ module Postgres
5
+
6
+ end
@@ -0,0 +1,21 @@
1
+ require 'postgres/clone/logger'
2
+
3
+ module Postgres
4
+ module Clone
5
+ module CommandLine
6
+ include Logger
7
+
8
+ def build_command(command, sudo: false, user: nil)
9
+ if sudo
10
+ "sudo#{user.nil? ? '' : " -u #{user}"} #{command}"
11
+ else
12
+ command
13
+ end
14
+ end
15
+
16
+ def log_command(host_name, command)
17
+ log_message(Rainbow("[#{host_name}] Executing: #{command}").darkgray)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ module Postgres
2
+ module Clone
3
+ class CommandResult
4
+ attr_reader :exit_code, :output
5
+
6
+ def initialize(exit_code:, output:)
7
+ @exit_code = exit_code
8
+ @output = output.strip
9
+ end
10
+
11
+ def failed?
12
+ !success?
13
+ end
14
+
15
+ def success?
16
+ exit_code == 0
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,211 @@
1
+ require 'etc'
2
+ require 'postgres/clone/command_result'
3
+ require 'postgres/clone/local_commands'
4
+ require 'postgres/clone/logger'
5
+ require 'postgres/clone/remote_commands'
6
+
7
+ module Postgres; module Clone;
8
+ module Commands
9
+ include Logger
10
+ include LocalCommands
11
+ include RemoteCommands
12
+
13
+ def copy_file(src_host, dst_host, file_path)
14
+ log_message("Copying #{file_path} from #{src_host} to #{dst_host}", header: '')
15
+
16
+ if src_host == dst_host
17
+ log_message(src_host, 'Source and destination host are the same, skipping file copy', color: :yellow, header: '')
18
+ return
19
+ end
20
+
21
+ result =
22
+ if local_host?(src_host)
23
+ if file_exists?(dst_host, file_path)
24
+ puts Rainbow("[#{dst_host}] File #{file_path} already exists!").green
25
+ return if %w(yes y).include?(
26
+ ask(Rainbow('Would you like to restore the existing dump (y/n)?').yellow).downcase
27
+ )
28
+ abort Rainbow("[#{dst_host}] File #{file_path} already exists!").red
29
+ else
30
+ run_local("scp #{file_path} #{dst_host}:#{file_path}")
31
+ end
32
+ elsif local_host?(dst_host)
33
+ if file_exists?('localhost', file_path)
34
+ puts Rainbow("[localhost] File #{file_path} already exists!").green
35
+ return if %w(yes y).include?(
36
+ ask(Rainbow('Would you like to restore the existing dump (y/n)?').yellow).downcase
37
+ )
38
+ abort Rainbow("[localhost] File #{file_path} already exists!").red
39
+ else
40
+ run_local("scp #{src_host}:#{file_path} #{file_path}")
41
+ end
42
+ else
43
+ if file_exists?(dst_host, file_path)
44
+ puts Rainbow("[#{dst_host}] File #{file_path} already exists!").green
45
+ return if %w(yes y).include?(
46
+ ask(Rainbow('Would you like to restore the existing dump (y/n)?').yellow).downcase
47
+ )
48
+ abort Rainbow("[#{dst_host}] File #{file_path} already exists!").red
49
+ else
50
+ run_remote(src_host, "scp #{file_path} #{dst_host}:#{file_path}")
51
+ end
52
+ end
53
+
54
+ abort Rainbow('The file copy failed, cancelling database clone').red if result.failed?
55
+ end
56
+
57
+ def current_user
58
+ # TODO: remote
59
+ Etc.getlogin
60
+ end
61
+
62
+ def disk_usage(host_name)
63
+ result =
64
+ if local_host?(host_name)
65
+ run_local(free_disk_space_command)
66
+ else
67
+ run_remote(host_name, free_disk_space_command)
68
+ end
69
+
70
+ columns = result.output.split(/\s+/)
71
+
72
+ abort Rainbow('Expected at least 5 columns of data from df -H').red unless columns.length >= 5
73
+
74
+ { available: columns[3], size: columns[1] }
75
+ end
76
+
77
+ def file_exists?(host_name, file_path)
78
+ log_message("Checking file existance: #{file_path}", host_name: host_name)
79
+
80
+ result =
81
+ if local_host?(host_name)
82
+ run_local(file_exists_command(file_path))
83
+ else
84
+ run_remote(host_name, file_exists_command(file_path))
85
+ end
86
+
87
+ result.success?
88
+ end
89
+
90
+ def file_exists_command(file_path)
91
+ "test -e #{file_path}"
92
+ end
93
+
94
+ def free_disk_space_command
95
+ 'df -H / | grep "/$"'
96
+ end
97
+
98
+ def local_host?(host_name)
99
+ host_name == 'localhost'
100
+ end
101
+
102
+ def postgres_create_database(host_name, database_name, template: nil)
103
+ log_message("Creating database #{database_name}", host_name: host_name)
104
+
105
+ result =
106
+ if local_host?(host_name)
107
+ run_local(
108
+ postgres_create_database_command(database_name, template: template)
109
+ )
110
+ else
111
+ sudo_remote(
112
+ host_name,
113
+ postgres_create_database_command(database_name, template: template),
114
+ user: 'postgres'
115
+ )
116
+ end
117
+
118
+ abort Rainbow('Failed to create database').red if result.failed?
119
+ end
120
+
121
+ def postgres_create_database_command(database_name, template: nil)
122
+ "psql -c \"create database #{database_name}#{template.nil? ? '' : " with template #{template}"}\""
123
+ end
124
+
125
+ def postgres_dump_database(host_name, database_name, file_path)
126
+ log_message("Dumping database #{database_name} to #{file_path}", header: '', host_name: host_name)
127
+
128
+ if file_exists?(host_name, file_path)
129
+ puts Rainbow("[#{host_name}] File #{file_path} already exists!").green
130
+ use_existing_file = %w(yes y).include?(
131
+ ask(Rainbow('Would you like to restore the existing dump (y/n)?').yellow).downcase
132
+ )
133
+ return if use_existing_file
134
+ abort Rainbow("Please delete #{host_name}:#{file_path} before continuing").red
135
+ end
136
+
137
+ result =
138
+ if local_host?(host_name)
139
+ run_local(
140
+ postgres_dump_database_command(file_path, database_name)
141
+ )
142
+ else
143
+ sudo_remote(
144
+ host_name,
145
+ postgres_dump_database_command(file_path, database_name),
146
+ user: 'postgres'
147
+ )
148
+ end
149
+
150
+ if result.failed?
151
+ puts Rainbow('The database dump failed, cleaning up partial dump file').red
152
+ if local_host?(host_name)
153
+ sudo_local("rm #{file_path}")
154
+ else
155
+ sudo_remote(host_name, "rm #{file_path}")
156
+ end
157
+ abort Rainbow('Cancelling database clone').red
158
+ end
159
+ end
160
+
161
+ def postgres_dump_database_command(file_path, source_database)
162
+ "pg_dump --file=#{file_path} --no-acl --no-owner --format=custom #{source_database}"
163
+ end
164
+
165
+ def postgres_restore_database(host_name, database_name, file_path)
166
+ log_message("Restoring database #{database_name} from #{file_path}", header: '', host_name: host_name)
167
+
168
+ abort Rainbow("File #{file_path} does not exist!").red unless file_exists?(host_name, file_path)
169
+
170
+ # TODO: check to see if target database exists
171
+
172
+ postgres_create_database(host_name, database_name)
173
+
174
+ result =
175
+ if local_host?(host_name)
176
+ run_local(
177
+ postgres_restore_database_command(file_path, database_name)
178
+ )
179
+ else
180
+ sudo_remote(
181
+ host_name,
182
+ postgres_restore_database_command(file_path, database_name),
183
+ user: 'postgres'
184
+ )
185
+ end
186
+
187
+ abort Rainbow('The database restore failed').red if result.failed?
188
+ end
189
+
190
+ def postgres_restore_database_command(file_path, destination_database)
191
+ "pg_restore --dbname=#{destination_database} --jobs=4 -O #{file_path}"
192
+ end
193
+
194
+ def print_host_disk_space(host_name)
195
+ log_message('Checking free disk space', header: '', host_name: host_name)
196
+
197
+ usage = disk_usage(host_name)
198
+
199
+ log_message("#{usage[:available]} free of #{usage[:size]} total", host_name: host_name)
200
+ end
201
+
202
+ def user_password(host_name, user)
203
+ @user_passwords ||= {}
204
+ @user_passwords["#{host_name}_#{user}"] ||= begin
205
+ ask(Rainbow("#{user}'s password for #{host_name}? ").yellow, echo: false).tap do |_|
206
+ puts
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end; end;
@@ -0,0 +1,76 @@
1
+ require 'thor'
2
+
3
+ require 'postgres/clone/commands'
4
+
5
+ module Postgres; module Clone;
6
+ class Executable < Thor
7
+ include Commands
8
+
9
+ option :src_host, required: true
10
+ option :src_db, required: true
11
+ option :dst_host, required: true
12
+ option :dst_db
13
+ option :file_path
14
+ option :verbose, default: true
15
+ desc 'clone_database', 'Clones a Postgres database from one host to another'
16
+ def clone_database
17
+ print_hosts_disk_space
18
+
19
+ if options[:src_host] == options[:dst_host]
20
+ create_database_using_template
21
+ else
22
+ create_database_using_restore
23
+ end
24
+
25
+ print_hosts_disk_space
26
+
27
+ close_ssh_connections
28
+ end
29
+
30
+ default_task :clone_database
31
+
32
+ private
33
+
34
+ def create_database_using_restore
35
+ file_path = options[:file_path] || "/tmp/#{file_name}"
36
+
37
+ postgres_dump_database(options[:src_host], options[:src_db], file_path)
38
+
39
+ copy_file(options[:src_host], options[:dst_host], file_path)
40
+
41
+ postgres_restore_database(
42
+ options[:dst_host],
43
+ options[:dst_db] || file_name,
44
+ file_path
45
+ )
46
+
47
+ log_message('Finished restoring database!', header: '', color: :green)
48
+ end
49
+
50
+ def create_database_using_template
51
+ postgres_create_database(
52
+ options[:src_host],
53
+ options[:dst_db] || file_name,
54
+ template: options[:src_db]
55
+ )
56
+
57
+ log_message('Finished creating database!', header: '', color: :green)
58
+ end
59
+
60
+ def file_name
61
+ @file_name ||= begin
62
+ if options[:file_path].nil?
63
+ "#{options[:src_db]}_#{Time.now.strftime('%Y%m%d')}"
64
+ else
65
+ File.basename(options[:file_path])
66
+ end
67
+ end
68
+ end
69
+
70
+ def print_hosts_disk_space
71
+ [options[:src_host], options[:dst_host]].uniq.each do |host_name|
72
+ print_host_disk_space(host_name)
73
+ end
74
+ end
75
+ end
76
+ end; end;
@@ -0,0 +1,22 @@
1
+ require 'postgres/clone/command_line'
2
+ require 'postgres/clone/command_result'
3
+ require 'postgres/clone/logger'
4
+
5
+ module Postgres; module Clone;
6
+ module LocalCommands
7
+ include CommandLine
8
+
9
+ def run_local(command, sudo: false, user: nil)
10
+ actual_command = build_command(command, sudo: sudo, user: user)
11
+ log_command('localhost', actual_command)
12
+
13
+ output = `#{actual_command}`
14
+
15
+ CommandResult.new(exit_code: $?.exitstatus, output: output)
16
+ end
17
+
18
+ def sudo_local(command, user: nil)
19
+ run_local(command, sudo: true, user: user)
20
+ end
21
+ end
22
+ end; end;
@@ -0,0 +1,14 @@
1
+ require 'rainbow'
2
+
3
+ module Postgres
4
+ module Clone
5
+ module Logger
6
+ DEFAULT_COLOR = :blue
7
+
8
+ def log_message(message, color: DEFAULT_COLOR, header: nil, host_name: nil)
9
+ puts header unless header.nil?
10
+ puts Rainbow("#{host_name.nil? ? '' : "[#{host_name}] "}#{message}").color(color)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ require 'net/ssh'
2
+ require 'postgres/clone/command_line'
3
+ require 'postgres/clone/command_result'
4
+ require 'postgres/clone/logger'
5
+
6
+ module Postgres; module Clone;
7
+ module RemoteCommands
8
+ include CommandLine
9
+ include Logger
10
+
11
+ def close_ssh_connections
12
+ (@ssh_connections || {}).values.each(&:close)
13
+ end
14
+
15
+ def open_ssh_connection(host_name, user)
16
+ @ssh_connections ||= {}
17
+ @ssh_connections[host_name] ||= begin
18
+ log_message("Opening ssh connection to #{host_name} as #{user}")
19
+ Net::SSH.start(host_name, user)
20
+ end
21
+ end
22
+
23
+ def run_remote(host_name, command, user: current_user, sudo: false)
24
+ result_attributes = { exit_code: nil, output: '' }
25
+
26
+ ssh = open_ssh_connection(host_name, user)
27
+ ssh.open_channel do |channel|
28
+ channel.request_pty { |_, success| abort('could not obtain pty') unless success }
29
+
30
+ actual_command = build_command(command, user: user, sudo: sudo)
31
+ log_message(actual_command, host_name: host_name, color: :gray)
32
+
33
+ channel.exec(actual_command) do |_, success|
34
+ abort('could not execute command') unless success
35
+
36
+ channel.on_data do |_, data|
37
+ puts data
38
+ case data
39
+ when /^\[sudo\] password for (.+):/i
40
+ password = user_password(host_name, $1)
41
+ log_message("Sending sudo password for #{user}", host_name: host_name, color: :gray)
42
+ channel.send_data("#{password}\n")
43
+ when /(.+)@(.+)'s password:/i
44
+ password = user_password($2, $1)
45
+ log_message("Sending user password for #{$1}", host_name: host_name, color: :gray)
46
+ channel.send_data("#{password}\n")
47
+ when /are you sure you want to continue connecting \(yes\/no\)\?/i
48
+ log_message('ignoring key warning', host_name: host_name, color: :yellow)
49
+ channel.send_data("yes\n")
50
+ else
51
+ result_attributes[:output] += data
52
+ end
53
+ end
54
+
55
+ channel.on_extended_data do |_, _, data|
56
+ log_message("stderr: #{data}", host_name: host_name, color: :red)
57
+ end
58
+
59
+ channel.on_request('exit-status') do |_, data|
60
+ result_attributes[:exit_code] = data.read_long
61
+ end
62
+ end
63
+ end
64
+
65
+ ssh.loop
66
+
67
+ CommandResult.new(result_attributes)
68
+ end
69
+
70
+ def sudo_remote(host_name, command, user: nil)
71
+ run_remote(host_name, command, user: user, sudo: true)
72
+ end
73
+ end
74
+ end; end;
@@ -0,0 +1,5 @@
1
+ module Postgres
2
+ module Clone
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'postgres/clone/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'postgres-clone'
8
+ spec.version = Postgres::Clone::VERSION
9
+ spec.authors = ['Josh Rickard']
10
+ spec.email = ['josh.rickard@gmail.com']
11
+
12
+ spec.summary = 'A command line utility for copying postgres databases.'
13
+ spec.description = 'A command line utility for copying postgres databases. Wraps pg_backup and pg_restore in a Ruby wrapper.'
14
+ spec.homepage = 'https://github.com/joshrickard/postgres-clone'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = 'pg-clone'
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'highline', '~> 1.7', '>= 1.7.8'
23
+ spec.add_dependency 'net-ssh', '~> 3.1', '>= 3.1.1'
24
+ spec.add_dependency 'rainbow', '~> 2.1', '>= 2.1.0'
25
+ spec.add_dependency 'thor', '~> 0.19', '>= 0.19.1'
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.11'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.0'
30
+ end
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: postgres-clone
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Josh Rickard
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-05-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: highline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.7.8
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.7'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.7.8
33
+ - !ruby/object:Gem::Dependency
34
+ name: net-ssh
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 3.1.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.1.1
53
+ - !ruby/object:Gem::Dependency
54
+ name: rainbow
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '2.1'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.1.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '2.1'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.1.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: thor
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '0.19'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.19.1
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.19'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 0.19.1
93
+ - !ruby/object:Gem::Dependency
94
+ name: bundler
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '1.11'
100
+ type: :development
101
+ prerelease: false
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: '1.11'
107
+ - !ruby/object:Gem::Dependency
108
+ name: rake
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '10.0'
114
+ type: :development
115
+ prerelease: false
116
+ version_requirements: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: '10.0'
121
+ - !ruby/object:Gem::Dependency
122
+ name: rspec
123
+ requirement: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '3.0'
128
+ type: :development
129
+ prerelease: false
130
+ version_requirements: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '3.0'
135
+ description: A command line utility for copying postgres databases. Wraps pg_backup
136
+ and pg_restore in a Ruby wrapper.
137
+ email:
138
+ - josh.rickard@gmail.com
139
+ executables:
140
+ - pg-clone
141
+ extensions: []
142
+ extra_rdoc_files: []
143
+ files:
144
+ - ".gitignore"
145
+ - ".rspec"
146
+ - ".travis.yml"
147
+ - Gemfile
148
+ - LICENSE.txt
149
+ - README.md
150
+ - Rakefile
151
+ - bin/console
152
+ - bin/setup
153
+ - exe/pg-clone
154
+ - lib/postgres-clone.rb
155
+ - lib/postgres/clone/command_line.rb
156
+ - lib/postgres/clone/command_result.rb
157
+ - lib/postgres/clone/commands.rb
158
+ - lib/postgres/clone/executable.rb
159
+ - lib/postgres/clone/local_commands.rb
160
+ - lib/postgres/clone/logger.rb
161
+ - lib/postgres/clone/remote_commands.rb
162
+ - lib/postgres/clone/version.rb
163
+ - postgres-clone.gemspec
164
+ homepage: https://github.com/joshrickard/postgres-clone
165
+ licenses:
166
+ - MIT
167
+ metadata: {}
168
+ post_install_message:
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubyforge_project:
184
+ rubygems_version: 2.5.1
185
+ signing_key:
186
+ specification_version: 4
187
+ summary: A command line utility for copying postgres databases.
188
+ test_files: []