vagrant-cloner 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vagrant-cloner.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Rob Yurkowski
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # vagrant-cloner
2
+
3
+ Copy down production resources to your new Vagrant VM.
4
+
5
+ ## Installation
6
+
7
+ Use vagrant's built-in gem system:
8
+
9
+ vagrant gem install vagrant-cloner
10
+
11
+ ## Usage
12
+
13
+ You will need to add a `config.vm.provision :cloner` section to your config in
14
+ order for Cloner to work. This should come after your other provisioners; if
15
+ Cloner runs before Chef or Puppet, for example, it's quite conceivable there
16
+ would be no database to restore to!
17
+
18
+ Each cloner has its own section inside the configuration, and this is the
19
+ recommended way to set them:
20
+
21
+ ``` ruby
22
+ Vagrant::Config.run do |config|
23
+
24
+ config.vm.provision :chef_solo do |chef|
25
+ # ...
26
+ end
27
+
28
+ config.vm.provision :cloner do |cfg|
29
+ cfg.cloners.mysql.tap do |c|
30
+ # Set options here.
31
+ c.enabled = true
32
+ c.run_order = 10
33
+ # ...
34
+ end
35
+ end
36
+ end
37
+ ```
38
+
39
+ The following keys are valid:
40
+
41
+ - **cloners**
42
+ - **(all cloners)**
43
+ - **enabled** - Required: Boolean whether to use this cloner or not. Defaults to false.
44
+ - **run_order** - Suggested: Integer value that dictates which order cloners run in. Lower orders run first. Defaults to 1000.
45
+ - **mysql**
46
+ - **remote_host** - String containing the remote server's FQDN.
47
+ - **remote_user** - Username to connect to remote server.
48
+ - **remote_password** - Optional: Password to connect to remote server. (Can be ignored if using publickey auth.)
49
+ - **remote_db_user** - Username to remote database server.
50
+ - **remote_db_password** - Password to remote database server.
51
+ - **vm_db_user** - Username to database server on VM.
52
+ - **vm_db_password** - Password to database server on VM.
53
+ - **databases_to_clone** - Optional: Array of databases to copy down. Defaults to 'all'.
54
+ - **remote_backup_path** - Optional: Where to dump databases to on remote server. Defaults to '/tmp'.
55
+ - **local_backup_path** - Optional: Where to store databases on host machine. Defaults to '/tmp'.
56
+ - **vm_backup_path** - Optional: Where to upload databases on VM. Defaults to '/tmp'.
57
+ - **backup_file** - Optional: Name for database dump. Defaults to mysql-dump-YYYY-MM-DD.sql.
58
+ - **disable_cleanup** - Optional: Don't remove database dumps after completion. Defaults to false.
59
+ - **testcloner**
60
+ - **foo** - String containing a message to print to console.
61
+ - **mysqlcleaner**
62
+ - **scripts** -- Array containing strings of URLs of remote SQL files to be run against the VM's database.
63
+ - **vm_db_user** - Username to database server on VM.
64
+ - **vm_db_password** - Password to database server on VM.
65
+
66
+ If you have some concern about storing passwords in this file (i.e. your Vagrantfile
67
+ is under version control), remember that the Vagrantfile is fully executed, so you can
68
+ simply require a file from elsewhere or read values in.
69
+
70
+ ## Current List of Supplied Cloners
71
+
72
+ - `mysql` - Import a MySQL database(s)
73
+ - `testcloner` - A simple example of a cloner not meant for use.
74
+ - `mysqlcleaner` - Runs arbitrary SQL scripts against the MySQL server. Useful for sanitizing databases imported by the `mysql` cloner.
75
+
76
+ ## Extra Cloners
77
+
78
+ You can write your own cloners to use with the tool. Unfortunately, because of how Vagrant loads its configuration settings, it's not possible to store these in a directory that is not in the gem itself.
79
+
80
+ Our suggestion is as follows:
81
+
82
+ 1. Fork the gem and git-clone it;
83
+ 2. Add your own cloner inside lib/vagrant-cloner/cloners/;
84
+ 3. Add your configuration settings inside your Vagrantfile;
85
+ 4. Run `rake build`;
86
+ 5. Run `vagrant gem install vagrant-cloner --local ./pkg/`
87
+ 6. Resume using vagrant as usual.
88
+
89
+ If you make an error in your script, you may have a hard time uninstalling it with `vagrant gem uninstall`. In a trice, you can remove directories in `~/.vagrant.d/gems/gems/` to manually remove troublesome gems. (Note that this was tested on a Linux distribution, so this may vary for Mac and Windows users.)
90
+
91
+ ## How to Write a Cloner
92
+
93
+ To operate as a cloner, a class must inherit from `Vagrant::Cloners::Cloner`, and implement at a bare minimum these methods:
94
+
95
+ - `name` - Returns a string representation of the cloner's name; used for namespacing config.
96
+ - `validate!(env, errors)` - Can be used to call `errors.add` if there are validations that need to be performed on configuration values.
97
+ - `call` - Executes the cloner's routine.
98
+
99
+ A cloner must also be registered in the config to be run. This is best done after the class has been closed, at the bottom of the file:
100
+
101
+ `Vagrant::Provisioners::Cloner::ClonerConfig.register_cloner <Class>.instance.name, <Class>.instance`
102
+
103
+ So for the MySQL cloner (which is `Vagrant::Cloners::MysqlCloner`), the line would read
104
+
105
+ `Vagrant::Provisioners::Cloner::ClonerConfig.register_cloner Vagrant::Cloners::MysqlCloner.instance.name, Vagrant::Cloners::MysqlCloner.instance`
106
+
107
+ A very minimal example [can be found in the cloners directory](lib/vagrant-cloner/cloners/testcloner.rb). For more detailed examples, look at the other cloners there!
108
+
109
+ ### How is this possibly useful for me?
110
+
111
+ `Cloner` exposes the `ssh`, `scp`, and `vm` methods to your class, so, in combination with `Kernel#system`, you can do pretty much anything on either host, VM, or remote server that you can do in (z|ba)sh.
112
+
113
+ The `vm` method, as an aside, is just a reference to the SSH communicator of Vagrant, so you can see what it provides [here](https://github.com/mitchellh/vagrant/blob/master/plugins/communicators/ssh/communicator.rb). If you need to actually access the environment, that is made available through the `env` method.
114
+
115
+ ## Contributing
116
+
117
+ 1. Fork it
118
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
119
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
120
+ 4. Push to the branch (`git push origin my-new-feature`)
121
+ 5. Create new Pull Request
122
+ 6. Explain why you think this belongs in the code here, and not inside your own gem that requires this one.
123
+
124
+ Pull requests most graciously accepted.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ require 'vagrant'
2
+ require 'vagrant-cloner/provisioner'
3
+ require 'vagrant-cloner/config'
4
+ require 'vagrant-cloner/cloner'
5
+
6
+ Dir[File.join(File.dirname(__FILE__), 'vagrant-cloner', 'cloners', '*.rb')].each {|f| require f }
7
+
8
+ Vagrant.provisioners.register(:cloner, Vagrant::Provisioners::Cloner)
@@ -0,0 +1,72 @@
1
+ require 'net/ssh'
2
+ require 'net/scp'
3
+ require 'date'
4
+ require 'singleton'
5
+
6
+ module Vagrant
7
+ module Cloners
8
+ # Cloner defines the base API that a cloner has to modify a VM or remote systems.
9
+ # TODO: Create a method that downloads to local and then uploads to VM?
10
+
11
+ class Cloner
12
+ include Singleton
13
+
14
+ attr_accessor :enabled, :env, :options, :run_order
15
+
16
+ def name
17
+ raise "Cloner must define #name and return a string."
18
+ end
19
+
20
+ def validate!(env, errors)
21
+ true
22
+ end
23
+
24
+ def enabled?
25
+ @enabled.nil? ? false : @enabled
26
+ end
27
+
28
+ def run_order
29
+ @run_order ||= 1000
30
+ end
31
+
32
+ # Shorthand for addressing the SSH communicator to the VM. View available methods
33
+ # here:
34
+ # https://github.com/mitchellh/vagrant/blob/master/plugins/communicators/ssh/communicator.rb
35
+ def vm
36
+ env[:vm].channel
37
+ end
38
+
39
+ # Opens an SSH connection to an arbitrary server and passes it to a supplied block.
40
+ #
41
+ # See netssh documentation for further method options.
42
+ # http://net-ssh.github.com/net-ssh/
43
+ def ssh(*args, &block)
44
+ Net::SSH.start(*args, &block)
45
+ end
46
+
47
+ # Opens an SCP connection to an arbitrary server, downloading or uploading to the
48
+ # machine currently in scope (whether the host machine or the VM).
49
+ # Recommended params:
50
+ # remote_host, remote_user, {:password => remote_password}
51
+ #
52
+ # See netscp documentation for further method options.
53
+ # https://github.com/net-ssh/net-scp
54
+ def scp(*args, &block)
55
+ Net::SCP.start(*args, &block)
56
+ end
57
+
58
+ def datestring
59
+ Date.today.to_s
60
+ end
61
+ protected :datestring
62
+
63
+ # Wrap debugging options.
64
+ %w(info warn error success).each do |meth|
65
+ define_method(meth) do |message|
66
+ env[:ui].send(meth.to_sym, message)
67
+ end
68
+ protected meth.to_sym
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,125 @@
1
+ module Vagrant
2
+ module Cloners
3
+ class MysqlCloner < Cloner
4
+
5
+ attr_accessor :remote_host, :remote_user, :remote_password,
6
+ :remote_db_user, :remote_db_password,
7
+ :vm_db_user, :vm_db_password,
8
+ :databases_to_clone,
9
+ :remote_backup_path, :local_backup_path, :vm_backup_path, :backup_file,
10
+ :disable_cleanup, :warned_about_password
11
+
12
+ def name
13
+ "mysql"
14
+ end
15
+
16
+ def validate!(env, errors)
17
+ errors.add "Must specify a remote user and host." unless remote_user && remote_host
18
+ errors.add "Must specify a remote database user and password." unless remote_db_user && remote_db_password
19
+ unless warned_about_password or remote_password
20
+ env.ui.warn "You haven't specified a remote password. Pulling down MySQL databases may fail unless you have proper publickey authentication enabled."
21
+ @warned_about_password = true
22
+ end
23
+ end
24
+
25
+ def remote_credentials
26
+ return @remote_credentials unless @remote_credentials.nil?
27
+
28
+ creds = [@remote_host, @remote_user]
29
+ creds.push :password => remote_password if remote_password
30
+ creds
31
+ end
32
+
33
+ def databases_to_clone
34
+ @databases_to_clone ||= []
35
+ end
36
+
37
+ def remote_backup_path
38
+ @remote_backup_path ||= "/tmp"
39
+ end
40
+
41
+ def local_backup_path
42
+ @local_backup_path ||= "/tmp"
43
+ end
44
+
45
+ def vm_backup_path
46
+ @vm_backup_path ||= "/tmp"
47
+ end
48
+
49
+ def disable_cleanup
50
+ @disable_cleanup.nil? ? false : @disable_cleanup
51
+ end
52
+ alias_method :disable_cleanup?, :disable_cleanup
53
+
54
+
55
+ def extract_relevant_options
56
+ @enabled = options.enabled
57
+ @remote_host = options.remote_host
58
+ @remote_user = options.remote_user
59
+ @remote_password = options.remote_password
60
+ @remote_db_user = options.remote_db_user
61
+ @remote_db_password = options.remote_db_password
62
+
63
+ @databases_to_clone = options.databases_to_clone
64
+
65
+ @vm_db_user = options.vm_db_user
66
+ @vm_db_password = options.vm_db_password
67
+
68
+ @remote_credentials = options.remote_credentials
69
+
70
+ @remote_backup_path = options.remote_backup_path
71
+ @local_backup_path = options.local_backup_path
72
+ @vm_backup_path = options.vm_backup_path
73
+ @backup_file = options.backup_file || "mysql-backup-#{datestring}.sql"
74
+
75
+ @remote_backup_location = File.join(@remote_backup_path, @backup_file)
76
+ @local_backup_location = File.join(@local_backup_path, @backup_file)
77
+ @vm_backup_location = File.join(@vm_backup_path, @backup_file)
78
+ end
79
+
80
+ def mysql_database_flag
81
+ if @databases_to_clone.include?("*") || @databases_to_clone.empty?
82
+ "--all-databases"
83
+ else
84
+ "--databases #{Array(@databases_to_clone).join(' ')}"
85
+ end
86
+ end
87
+
88
+ def call
89
+ extract_relevant_options
90
+ dump_remote_database
91
+ download_remote_database
92
+ import_database
93
+ clean_up
94
+ end
95
+
96
+ def dump_remote_database
97
+ command = "mysqldump -u#{@remote_db_user} -p#{@remote_db_password} #{mysql_database_flag} > #{@remote_backup_location}"
98
+ ssh(*remote_credentials) {|s| s.exec! command }
99
+ info "Finished dumping database!"
100
+ end
101
+
102
+ def download_remote_database
103
+ scp(*remote_credentials) {|s| s.download! @remote_backup_location, @local_backup_path }
104
+ info "Finished downloading file."
105
+ end
106
+
107
+ def import_database
108
+ vm.upload @local_backup_location, @vm_backup_location
109
+ vm.execute "mysql -u#{@vm_db_user} -p#{@vm_db_password} < #{@vm_backup_location}"
110
+ info "Done loading database."
111
+ end
112
+
113
+ def clean_up
114
+ unless disable_cleanup?
115
+ ssh(*remote_credentials) {|s| s.exec! "rm #{@remote_backup_location}" } and info "Removed remote backup file."
116
+ system "rm #{@local_backup_location}" and info "Removed local backup file."
117
+ vm.execute "rm #{@vm_backup_location}" and info "Removed vm backup file."
118
+ info "Done!"
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ Vagrant::Provisioners::Cloner::ClonerConfig.register_cloner Vagrant::Cloners::MysqlCloner.instance.name, Vagrant::Cloners::MysqlCloner.instance
@@ -0,0 +1,37 @@
1
+ module Etailer
2
+ class MysqlCleanerCloner < ::Vagrant::Cloners::Cloner
3
+ attr_accessor :vm_db_user, :vm_db_password
4
+
5
+ def name
6
+ "mysqlcleaner"
7
+ end
8
+
9
+ def scripts
10
+ @scripts ||= []
11
+ end
12
+
13
+ def scripts=(scripts)
14
+ @scripts = Array(scripts).flatten
15
+ end
16
+
17
+ def validate!(env, errors)
18
+ errors.add("You have to specify at least one script to run!") if scripts.nil? || scripts.empty?
19
+ errors.add("You must specify a VM database user and password!") unless vm_db_user && vm_db_password
20
+ end
21
+
22
+ def mysql_connection_string
23
+ "-u#{vm_db_user} -p#{vm_db_password}"
24
+ end
25
+
26
+ def call
27
+ @scripts.each do |script|
28
+ info "Fetching script #{script}..."
29
+ basename = script.split("/").last
30
+ vm.execute "wget #{script} -P /tmp"
31
+ vm.execute "mysql #{mysql_connection_string} < /tmp/#{basename}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ Vagrant::Provisioners::Cloner::ClonerConfig.register_cloner Etailer::MysqlCleanerCloner.instance.name, Etailer::MysqlCleanerCloner.instance
@@ -0,0 +1,31 @@
1
+ module Vagrant
2
+ module Cloners
3
+ class TestCloner < Cloner
4
+ attr_accessor :foo
5
+
6
+ def name
7
+ "testcloner"
8
+ end
9
+
10
+ def validate!(env, errors)
11
+ errors.add("Must specify a foo") unless foo
12
+ end
13
+
14
+ def call
15
+ info "Foo is #{foo}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ Vagrant::Provisioners::Cloner::ClonerConfig.register_cloner Vagrant::Cloners::TestCloner.instance.name, Vagrant::Cloners::TestCloner.instance
22
+
23
+ # Inside your vagrant file, make sure you add the section for this cloner!
24
+ # # ...
25
+ #
26
+ # config.vm.provision :cloner do |cfg|
27
+ # cfg.cloners.testcloner.tap do |c|
28
+ # c.enabled = true
29
+ # c.foo = "Roses are red, violets are foo."
30
+ # end
31
+ # end
@@ -0,0 +1,70 @@
1
+ # In here:
2
+ # cloners = OpenStruct.new
3
+ # cloners.mysql = MysqlCloner.config_class.new
4
+ #
5
+ # In readme:
6
+ # config.cloners.mysql.config_option = yes
7
+ #
8
+ # In provisioner
9
+ # config.cloners.any? {|k, v| v.enabled?}
10
+
11
+ require 'ostruct'
12
+
13
+ module Vagrant
14
+ module Provisioners
15
+ class ClonerContainer < OpenStruct
16
+ include Enumerable
17
+ def members
18
+ methods(false).grep(/=/).map {|m| m[0...-1] }
19
+ end
20
+
21
+ def each
22
+ members.each {|m| yield send(m) }
23
+ self
24
+ end
25
+
26
+ def each_pair
27
+ members.each {|m| yield m, send(m)}
28
+ self
29
+ end
30
+
31
+ def enabled_by_order
32
+ members.collect {|m| send(m)} # Get all plugin instances
33
+ .select {|m| m.enabled? } # Only enabled
34
+ .sort_by(&:run_order) # Sort by run order
35
+ .each {|m| yield m} # Yield up
36
+ end
37
+
38
+ end
39
+
40
+ class Cloner
41
+ class ClonerConfig < Vagrant::Config::Base
42
+ class << self
43
+ def registered_cloners
44
+ @@registered_cloners ||= ClonerContainer.new
45
+ end
46
+
47
+ def register_cloner(name, instance)
48
+ registered_cloners.send("#{name}=".to_sym, instance)
49
+ end
50
+ end
51
+
52
+ def cloners
53
+ @@registered_cloners
54
+ end
55
+
56
+ def validate(env, errors)
57
+ # errors.add('Remote server must be specified.') unless remote_host
58
+ cloners.select {|c| c.enabled? }.each do |cloner|
59
+ cloner.validate!(env, errors)
60
+ end
61
+ end
62
+ end
63
+
64
+ def self.config_class
65
+ Vagrant::Provisioners::Cloner::ClonerConfig
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,19 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ class Cloner < Vagrant::Provisioners::Base
4
+
5
+ def prepare
6
+ end
7
+
8
+ def provision!
9
+ env[:ui].info "Vagrant-Cloner beginning back-up process."
10
+ config.cloners.enabled_by_order do |cloner|
11
+ cloner.tap {|c|
12
+ c.options = config.cloners.send(cloner.name)
13
+ c.env = env
14
+ }.call
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1 @@
1
+ require 'vagrant-cloner'
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ version = '0.9.1'
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = "vagrant-cloner"
9
+ gem.version = version
10
+ gem.authors = ["Robert Coleman", "Rob Yurkowski"]
11
+ gem.email = ["github@robert.net.nz", "rob@yurkowski.net"]
12
+ gem.description = %q{Copy production resources down to your new VM.}
13
+ gem.summary = %q{Copy production resources down to your new VM.}
14
+ gem.homepage = "https://github.com/robyurkowski/vagrant-cloner/"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ # gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ # gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vagrant-cloner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Robert Coleman
9
+ - Rob Yurkowski
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-02-03 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Copy production resources down to your new VM.
16
+ email:
17
+ - github@robert.net.nz
18
+ - rob@yurkowski.net
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - .gitignore
24
+ - Gemfile
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - lib/vagrant-cloner.rb
29
+ - lib/vagrant-cloner/cloner.rb
30
+ - lib/vagrant-cloner/cloners/mysql.rb
31
+ - lib/vagrant-cloner/cloners/mysql_cleaner.rb
32
+ - lib/vagrant-cloner/cloners/testcloner.rb
33
+ - lib/vagrant-cloner/config.rb
34
+ - lib/vagrant-cloner/provisioner.rb
35
+ - lib/vagrant_init.rb
36
+ - vagrant-cloner.gemspec
37
+ homepage: https://github.com/robyurkowski/vagrant-cloner/
38
+ licenses: []
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubyforge_project:
57
+ rubygems_version: 1.8.24
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Copy production resources down to your new VM.
61
+ test_files: []