synchronize 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 backups.gemspec
4
+ gemspec
@@ -0,0 +1,65 @@
1
+ # DESCRIPTION
2
+
3
+ Synchronize is designed to automate the ugly tasks of restoring database dumps and assets from s3 to our different environments.
4
+
5
+ # INSTALL
6
+
7
+ ## Gemfile
8
+ Synchronize will be available to download as a gem in the [downloads](https://github.com/bebanjo/synchronize/downloads) section.
9
+
10
+ Get Latest: https://github.com/downloads/bebanjo/synchronize/synchronize-0.1.1.gem
11
+
12
+ ## From source
13
+
14
+ git clone git@github.com:bebanjo/synchronize.git
15
+ cd synchronize
16
+ gem build synchronize.gemspec
17
+ gem install synchronize-X.Y.Z.gem
18
+
19
+ ## Dependencies
20
+
21
+ * Synchronize requires duplicity and python boto to restore files from s3, you can install both running `synchronize dependencies`. It only does work on mac and requires brew.
22
+
23
+ # USAGE
24
+
25
+ ## Subcommands
26
+
27
+ Run synchronize followed by one of these sub-commands:
28
+
29
+ * `assets` Synchronizes the assets to the system directory
30
+ * `check` Perform a health check of system dependencies and environment variables required in order to run synchronize
31
+ * `db` Synchronizes a database with the latest s3 dbdump.
32
+ * `db2disk` Downloads the latest backup from s3 to local disk.
33
+ * `dependencies` Installs the command line tools required for synchronize to work. Requires brew.
34
+ * `help` Display global or [command] help documentation.
35
+
36
+ ## Environment variables, options and paths.
37
+
38
+ There are three of ways in which synchronize is able to get the information it needs to perform its tasks: trough command options, environment variables and knife. Command options has preference over knife, environment variables are always a last resource. Not all the information can be gathered trough knife (see below) so in these cases they should be present as options or environment variables. In any case the command helps you to find what you are missing. Run synchronize help to see every available option.
39
+
40
+ * Synchronize assumes that you are sitting it in your `RAILS_ROOT` directory when you run it (`if RAILS_ENV == development`). In other `RAILS_ENV` situations (`production`, `staging`, `preproduction`) Synchronize assumes that the directory structure where the app is sitting is `/home/APP_NAME/app/current`. The command also accepts a `RAILS_ROOT` env variable. Alternatively you need to provide mysql credentials through command line options.
41
+ * `RAILS_ENV` variable must be defined or `--rails_env` paramenter has to be used.
42
+ * When using `--knife` parameter, synchronize assumes that there is chef data bag (already created in our chef server) set up with the following structure:
43
+
44
+ ````
45
+ [
46
+ "buckets" : {
47
+ "id": "buckets",
48
+ "s3_backup_bucket": "VALUE"
49
+ },
50
+ "keys": {
51
+ "aws_access_key_id": "VALUE",
52
+ "id": "keys",
53
+ "aws_secret_access_key": "VALUE"
54
+ }
55
+ ]
56
+ ````
57
+ * Alternatively you can set the following environment variables: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_BUCKET` use the related command line options.
58
+ * You can run synchronize check to know what's missing to run synchronize.
59
+
60
+
61
+ # TODO / IMPROVEMENTS
62
+
63
+ * Refactor code, at this moment synchronize does not use any classes.
64
+ * Error parsing, output of some commands need to be parsed and synchronize the error output, also the verbosity of output on errors should be improved.
65
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'synchronize'
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ require 'fog'
4
+ require 'commander/import'
5
+ require 'synchronize/cli'
6
+ require 'synchronize/version'
7
+
8
+ program :name, "synchronize"
9
+ program :description, "Performs synchronization of the contents between different Rails environments."
10
+ program :version, VERSION
11
+
12
+ # Some nice defaults
13
+ RAILS_ROOT = Dir.pwd unless ENV['RAILS_ROOT']
14
+
15
+
16
+
@@ -0,0 +1,9 @@
1
+ require 'synchronize/tasks'
2
+
3
+ def duplicity_restore
4
+ puts " - Restoring assets from s3"
5
+ system("duplicity -verror --no-encryption --force restore s3+http://#{@bucket}/projects/#{@app_name}/files/#{@app_name} #{RAILS_ROOT}/public/system/")
6
+ puts $? != 0 ? "Assets restore unsuccessful." : "Assets restore successful."
7
+ end
8
+
9
+
@@ -0,0 +1,109 @@
1
+ require 'synchronize/tasks'
2
+ require 'synchronize/db'
3
+ require 'synchronize/utils'
4
+ require 'synchronize/assets'
5
+
6
+ default_command :all
7
+
8
+ command :all do |c|
9
+ c.syntax = "#{program(:name)}"
10
+ c.description = "Synchronizes assets and database."
11
+ c.option '-K', '--knife PATH', String, 'Path to knife config file'
12
+ c.option '-k', '--aws_accesskey KEY', String, 'aws secret access key.'
13
+ c.option '-I', '--aws_accessid KEY', String, 'aws access key id.'
14
+ c.option '-b', '--aws_bucket BUCKET', String, 'aws bucket name.'
15
+ c.option '-e', '--rails_env ENVIRONMENT', String, 'rails environment'
16
+ c.option '-u', '--mysql_user USERNAME (Defaults to root)', String, 'mysql user'
17
+ c.option '-p', '--mysql_pass PASSWORD (Defaults to empty)', String, 'mysql password'
18
+ c.option '-h', '--mysql_host HOSTNAME (Defaults to 127.0.0.1)', String, 'mysql hostname'
19
+ c.option '-d', '--database NAME', String, 'database'
20
+ c.action do |args, options|
21
+ @options = options
22
+ check_environment
23
+ get_rails_env
24
+ get_mysql_credentials
25
+ get_aws_credentials
26
+ get_app_name
27
+ get_production_db_name
28
+ get_latest_s3_dump
29
+ unzip_database
30
+ load_database
31
+ rm_dump
32
+ duplicity_restore
33
+ end
34
+ end
35
+
36
+ command :assets do |c|
37
+ c.syntax = "#{program(:name)} assets"
38
+ c.description = "Synchronizes the assets to the system directory"
39
+ c.option '-K', '--knife PATH', String, 'Path to knife config file'
40
+ c.option '-k', '--aws_accesskey KEY', String, 'aws secret access key.'
41
+ c.option '-I', '--aws_accessid KEY', String, 'aws access key id.'
42
+ c.option '-b', '--aws_bucket BUCKET', String, 'aws bucket name.'
43
+ c.action do |args, options|
44
+ @options = options
45
+ check_environment
46
+ get_aws_credentials
47
+ get_app_name
48
+ duplicity_restore
49
+ end
50
+ end
51
+
52
+ command :db do |c|
53
+ c.syntax = "#{program(:name)} db"
54
+ c.description = "Synchronizes a database with the latest s3 dbdump."
55
+ c.option '-K', '--knife PATH', String, 'Path to knife config file'
56
+ c.option '-k', '--aws_accesskey KEY', String, 'aws secret access key.'
57
+ c.option '-I', '--aws_accessid KEY', String, 'aws access key id.'
58
+ c.option '-b', '--aws_bucket BUCKET', String, 'aws bucket name.'
59
+ c.option '-e', '--rails_env ENVIRONMENT', String, 'rails environment'
60
+ c.option '-u', '--mysql_user USERNAME (Defaults to root)', String, 'mysql user'
61
+ c.option '-p', '--mysql_pass PASSWORD (Defaults to empty)', String, 'mysql password'
62
+ c.option '-h', '--mysql_host HOSTNAME (Defaults to 127.0.0.1)', String, 'mysql hostname'
63
+ c.option '-d', '--database NAME', String, 'database'
64
+ c.action do |args, options|
65
+ @options = options
66
+ check_environment
67
+ get_rails_env
68
+ get_mysql_credentials
69
+ get_aws_credentials
70
+ get_app_name
71
+ get_production_db_name
72
+ get_latest_s3_dump
73
+ unzip_database
74
+ load_database
75
+ rm_dump
76
+ end
77
+ end
78
+
79
+ command :db2disk do |c|
80
+ c.syntax = "#{program(:name)} db2disk"
81
+ c.description = "Downloads the latest backup from s3 to local disk."
82
+ c.action do |args, options|
83
+ check_environment
84
+ get_aws_credentials
85
+ get_app_name
86
+ get_production_db_name
87
+ get_latest_s3_dump
88
+ unzip_database
89
+ end
90
+ end
91
+
92
+ command :check do |c|
93
+ c.syntax = "#{program(:name)} check"
94
+ c.description = "Perform a health check of system dependencies and environment variables required in order to run synchronize"
95
+ c.action do |args, options|
96
+ check_environment
97
+ end
98
+ end
99
+
100
+ command :dependencies do |c|
101
+ c.syntax = "#{program(:name)} dependencies"
102
+ c.description = "Installs the command line tools required for synchronize to work. Requires brew."
103
+ c.action do |args, options|
104
+ install_dependencies
105
+ end
106
+ end
107
+
108
+
109
+
@@ -0,0 +1,64 @@
1
+ require 'synchronize/tasks'
2
+
3
+ def get_latest_s3_dump
4
+ puts " - Downloading #{@app_name} S3 dump."
5
+ connection = Fog::Storage.new(
6
+ :provider => 'AWS',
7
+ :aws_secret_access_key => @aws_secret_access_key,
8
+ :aws_access_key_id => @aws_access_key_id
9
+ )
10
+
11
+ directory = connection.directories.get(@bucket)
12
+ s3_file_url = "projects/#{@app_name}/dbdumps/#{@production_db}/latest.gz"
13
+ s3_file = directory.files.get(s3_file_url)
14
+
15
+ File.open("#{RAILS_ROOT}/#{@production_db}.gz", "w+") do |local_file|
16
+ local_file.write(s3_file.body)
17
+ end
18
+ end
19
+
20
+ def get_mysql_credentials
21
+ if @mysql_option
22
+ @db_user = @options.mysql_user || 'root'
23
+ @db_pass = @options.mysql_pass
24
+ @db_host = @options.mysql_host || '127.0.0.1'
25
+ @db_name = @options.database
26
+ else
27
+ credentials = Utils.load_yaml_file("#{RAILS_ROOT}/config/database.yml")
28
+ @db_user = credentials[@rails_env]['username'] || 'root'
29
+ @db_pass = credentials[@rails_env]['password']
30
+ @db_host = credentials[@rails_env]['host'] || '127.0.0.1'
31
+ @db_name = credentials[@rails_env]['database']
32
+ end
33
+ end
34
+
35
+ def unzip_database
36
+ puts
37
+ puts " - Unzipping #{@app_name} dump."
38
+ `gunzip -f #{RAILS_ROOT}/#{@production_db}.gz`
39
+ puts " - #{@app_name} dump sucessfully unzipped."
40
+ end
41
+
42
+ def load_database
43
+ puts " - Loading #{@app_name} dump into the database."
44
+
45
+ command_pre = "mysql -u #{@db_user} -h #{@db_host} "
46
+ command_in = @db_pass ? "-p#{@db_pass} " : String.new
47
+
48
+ command_post = "-e \"DROP DATABASE IF EXISTS #{@db_name}; CREATE DATABASE #{@db_name}\""
49
+ system(command_pre + command_in + command_post)
50
+
51
+ command_post = "#{@db_name} < #{RAILS_ROOT}/#{@production_db}"
52
+ system(command_pre + command_in + command_post)
53
+
54
+ puts $? != 0 ? " - There was a problem loading database #{@app_name}." : " - Database #{@app_name} sucessfully loaded."
55
+ end
56
+
57
+ def get_production_db_name
58
+ @production_db = "jorgesancha_#{@app_name}_production"
59
+ end
60
+
61
+ def rm_dump
62
+ `rm -f #{RAILS_ROOT}/#{@production_db}`
63
+ end
64
+
@@ -0,0 +1,170 @@
1
+ # -*- coding: utf-8 -*-
2
+ def get_app_name
3
+ @app_name = @rails_env == "development" ? RAILS_ROOT.split('/').last : RAILS_ROOT.split('/')[2]
4
+ end
5
+
6
+ def get_rails_env
7
+ @rails_env = @options.rails_env ? @options.rails_env : ENV['RAILS_ENV']
8
+ end
9
+
10
+ def get_knife_config
11
+ @knife_config = @options.knife ? @options.knife : ENV['KNIFE_CONFIG']
12
+ end
13
+
14
+ def get_databag(databag, key)
15
+ aws_yml = `unset BUNDLE_GEMFILE; cd /; knife data bag show #{databag} #{key} -F yml -c #{@knife_config}`
16
+ YAML.load(aws_yml)
17
+ end
18
+
19
+ def get_aws_credentials
20
+ if @knife_option
21
+ puts " - Obtaining AWS credentials from knife."
22
+
23
+ aws_data = get_databag('aws','keys')
24
+ @aws_access_key_id = aws_data['aws_access_key_id']
25
+ @aws_secret_access_key = aws_data['aws_secret_access_key']
26
+
27
+ aws_data = get_databag('aws','buckets')
28
+ @bucket = aws_data['s3_backup_bucket']
29
+
30
+ ENV['AWS_ACCESS_KEY_ID'] = @aws_access_key_id
31
+ ENV['AWS_SECRET_ACCESS_KEY'] = @aws_secret_access_key
32
+ elsif @aws_option
33
+ @bucket = @options.aws_bucket
34
+ @aws_access_key_id = @options.aws_accessid
35
+ @aws_secret_access_key = @options.aws_accesskey
36
+ ENV['AWS_ACCESS_KEY_ID'] = @aws_access_key_id
37
+ ENV['AWS_SECRET_ACCESS_KEY'] = @aws_secret_access_key
38
+ else
39
+ @bucket = ENV['AWS_BUCKET']
40
+ @aws_access_key_id = ENV['AWS_ACCESS_KEY_ID']
41
+ @aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
42
+ end
43
+ end
44
+
45
+ # Installs the command line tools required for synchronize.
46
+ def install_dependencies
47
+ puts " - Installing dependencies"
48
+ `brew install python`
49
+ `/usr/local/share/python/easy_install pip`
50
+ `/usr/local/share/python/pip install boto`
51
+ `brew install duplicity`
52
+ puts " - Synchronize dependencies installed"
53
+ end
54
+
55
+ def check_duplicity
56
+ unless `duplicity -V`
57
+ raise " - duplicity not installed or not in PATH. You can install it manually or run synchronize dependencies to install through brew"
58
+ else
59
+ puts " - Found duplicity command."
60
+ end
61
+ end
62
+
63
+ def check_knife_env
64
+ if ! ENV['KNIFE_CONFIG']
65
+ @knife_option = false
66
+ puts " - KNIFE_CONFIG variable or --knife parameter has not been set, falling back to AWS command line options or environment variables."
67
+ elsif ENV['KNIFE_CONFIG'] && ! File.exists?(ENV['KNIFE_CONFIG'])
68
+ raise " - KNIFE_CONFIG env variable exists but is not pointing to a proper knife config file, please point it to your knife config file."
69
+ else
70
+ puts " - knife properly configured."
71
+ @knife_option = true
72
+ end
73
+ end
74
+
75
+ def check_knife_option
76
+ @knife_option = true
77
+ if ! @options.knife
78
+ @knife_option = false
79
+ elsif @options.knife && ! File.exists?(@options.knife)
80
+ raise " - --knife is not pointing to a proper knife config file, please point it to your knife config file."
81
+ else
82
+ puts " - knife properly configured."
83
+ @knife_option = true
84
+ end
85
+ end
86
+
87
+ def check_aws_env
88
+ unless ENV['AWS_ACCESS_KEY_ID']
89
+ raise " - Environment variable AWS_ACCESS_KEY_ID not set."
90
+ end
91
+
92
+ unless ENV['AWS_SECRET_ACCESS_KEY']
93
+ raise " - Environment variable AWS_SECRET_ACCESS_KEY not set."
94
+ end
95
+
96
+ unless ENV['AWS_BUCKET']
97
+ raise " - Enviroment variable AWS_BUCKET not set."
98
+ end
99
+
100
+ puts " - AWS env variables present."
101
+ end
102
+
103
+ def check_aws_option
104
+ @aws_option = true if (@options.aws_accesskey && @options.aws_accessid && @options.aws_bucket)
105
+ end
106
+
107
+ def check_aws_knife
108
+ aws_data = get_databag('aws','buckets')
109
+ unless aws_data['s3_backup_bucket']
110
+ raise " - Could not get s3_backup_bucket parameter from chef databag aws"
111
+ end
112
+
113
+ aws_data = get_databag('aws','keys')
114
+ unless aws_data['aws_access_key_id']
115
+ raise " - Could not get aws_access_key_id parameter from chef databag aws"
116
+ end
117
+
118
+ unless aws_data['aws_secret_access_key']
119
+ raise " - Could not get aws_secret_access_key parameter from chef databag aws"
120
+ end
121
+ end
122
+
123
+ def check_rails_option
124
+ @rails_option = true if @options.rails_env
125
+ end
126
+
127
+ def check_rails_env
128
+ unless ENV['RAILS_ENV']
129
+ raise " - Neither environment variable RAILS_ENV nor --rails_env parameter has been set."
130
+ end
131
+ puts " - RAILS_ENV variable present."
132
+ end
133
+
134
+ def check_database_yml
135
+ unless File.exists?("#{RAILS_ROOT}/config/database.yml")
136
+ raise "database.yml not found, are you running synchronize on your RAILS_ROOT directory?"
137
+ end
138
+ puts " - Found database.yml config file"
139
+ end
140
+
141
+ def check_mysql_option
142
+ @mysql_option = true if @options.database
143
+ end
144
+
145
+ # "Tests the existence of the environment variables required for backups tasks"
146
+ def check_environment
147
+ puts " - Checking environment."
148
+ check_duplicity
149
+
150
+ check_knife_option
151
+ check_knife_env unless @knife_option
152
+ check_aws_option unless @knife_option
153
+
154
+ if (@knife_option && ! @aws_option)
155
+ get_knife_config
156
+ check_aws_knife
157
+ end
158
+
159
+ check_aws_env if (! @knife_option && ! @aws_option)
160
+
161
+ check_rails_option
162
+ check_rails_env unless @rails_option
163
+
164
+ check_mysql_option
165
+ check_database_yml unless @mysql_option
166
+
167
+ puts " - Environment check successful, you should be able to run synchronize without trouble."
168
+ end
169
+
170
+
@@ -0,0 +1,10 @@
1
+ class Utils
2
+ class << self
3
+ def load_yaml_file(filename)
4
+ require 'erb'
5
+
6
+ template = ERB.new(File.read(filename))
7
+ YAML.load(template.result)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Synchronize
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "synchronize/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "synchronize"
7
+ s.version = Synchronize::VERSION
8
+ s.authors = ["bebanjo support team"]
9
+ s.email = ["support@bebanjo.com"]
10
+ s.homepage = "https://github.com/bebanjo/synchronize"
11
+ s.summary = %q{ Performs synchronization of the contents between different Rails environments. }
12
+ s.description = %q{ Synchronize is designed to automate the ugly tasks of restoring database dumps and assets from s3 to our different environments. }
13
+
14
+ s.rubyforge_project = "synchronize"
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("fog", " >= 0.10.0" )
22
+ s.add_dependency("commander", " >= 4.0.6" )
23
+ s.add_dependency("chef", ">= 0.10")
24
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: synchronize
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - bebanjo support team
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-18 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: fog
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 55
29
+ segments:
30
+ - 0
31
+ - 10
32
+ - 0
33
+ version: 0.10.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: commander
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 51
45
+ segments:
46
+ - 4
47
+ - 0
48
+ - 6
49
+ version: 4.0.6
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: chef
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 31
61
+ segments:
62
+ - 0
63
+ - 10
64
+ version: "0.10"
65
+ type: :runtime
66
+ version_requirements: *id003
67
+ description: " Synchronize is designed to automate the ugly tasks of restoring database dumps and assets from s3 to our different environments. "
68
+ email:
69
+ - support@bebanjo.com
70
+ executables:
71
+ - synchronize
72
+ extensions: []
73
+
74
+ extra_rdoc_files: []
75
+
76
+ files:
77
+ - .gitignore
78
+ - Gemfile
79
+ - README.md
80
+ - Rakefile
81
+ - bin/synchronize
82
+ - lib/synchronize.rb
83
+ - lib/synchronize/assets.rb
84
+ - lib/synchronize/cli.rb
85
+ - lib/synchronize/db.rb
86
+ - lib/synchronize/tasks.rb
87
+ - lib/synchronize/utils.rb
88
+ - lib/synchronize/version.rb
89
+ - synchronize.gemspec
90
+ homepage: https://github.com/bebanjo/synchronize
91
+ licenses: []
92
+
93
+ post_install_message:
94
+ rdoc_options: []
95
+
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ hash: 3
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ hash: 3
113
+ segments:
114
+ - 0
115
+ version: "0"
116
+ requirements: []
117
+
118
+ rubyforge_project: synchronize
119
+ rubygems_version: 1.8.15
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: Performs synchronization of the contents between different Rails environments.
123
+ test_files: []
124
+