db-rotator 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba4a2bd4dffd3e671210454b4e7ba6dbb758ca07
4
- data.tar.gz: c9c7f9835d3807f3d302288844138330919a5942
3
+ metadata.gz: 07ce9c6a6b6f1da73a5e9e232173748c81f84673
4
+ data.tar.gz: cc3c9b96fe45aa4f715f92c3f4e22cd928e8d695
5
5
  SHA512:
6
- metadata.gz: 7abcb7f9c2aad35f1d891edb148182cb86e09f338a5b008bb8ab09386b93f25e87ed1d0d430f93447b57dd38ee51b06214e78a8f1f0ffdc662ee1264c5ef327f
7
- data.tar.gz: 03d563062c0940986b8f94e422869f1aa9ad22059820b3e354f98835d4db3b8246aaefdc18616790bef8a68fcfc9a9a362a857dccfadfbdd854c11c471747fc9
6
+ metadata.gz: 1d5a98d23de5eb850f8232e38beb2585f5eb2ade0eb91bb74a4b7562b53b51145451c6f7800fdeeff213f3f4c6e06cec5753b17a4efad43c745cbc6a9264d403
7
+ data.tar.gz: 883f046aec07acdddbee6cc09f3c9646674c380e910a0f655c5d028f6630f6533b9e6f716fd9b03c5725ec53bb08af50930c2f804e2d95652fd078e998cf6b84
data/README.md CHANGED
@@ -1,42 +1,58 @@
1
1
  # DBRotator
2
- Easy MySQL database rotation and pruning. Tell this utility where your nightly MySQL backup is, and it downloads, imports, and organizes the dump, pruning all but the newest N databases.
2
+ Easy MySQL database rotation and pruning -- downloads and imports a mysql dump, and then deletes all but N databases, keeping only the newest. This tool is geared toward rotating out achived MySQL backups to your local dev environment. So it works best when you have a most-current MySQL backup as a symlink that updates nightly.
3
3
 
4
4
  ## Installation
5
5
 
6
6
  `gem install db-rotator`
7
7
 
8
+ ## Requirements
9
+ - This tool creates and drops databases, and runs any SQL in your dump (duh), so you have to give it a user that can do all of that. If you're using this on a local dev environment, it's recommended to setup user/pass in `~/.my.cnf` so that "mysql" works without `-u` or `-p`. You can configure DBRotator to work with any credentials, however. See config section.
10
+ - Disk space for N+1 database instances, where N is the amount you want to prune to (maximum DBs).
11
+
8
12
  ## Usage
9
13
 
10
- 1. First off, the following is required:
11
- - SSH access to your most current MySQL backup as a single endpoint, like a symlink that updates nightly.
12
- - root access to destination MySQL instance. Recommended to setup username/password in `~/.my.cnf` so that "mysql" works without `-u` or `-p`.
14
+ **Minimal usage:**
15
+
16
+ Run: `db-rotator -p 'appdump_' -c 'scp db5:/opt/backups/latest.sql.bz2'`
17
+
18
+ **Or, from default config file** `~/.db-rotator.yml`:
19
+
20
+ ```
21
+ db_prefix: "appdump_"
22
+ scp_command: "scp db5:/opt/backups/latest.sql.bz2"
23
+ ```
24
+
25
+ Run: `db-rotator`
13
26
 
14
- 2. Configure the required options (see below). Configuration can be passed as command line arguments or set in `~/.db-rotator.yml`.
27
+ **Or, from a specific config file** `/whatever/rotator-config.conf`:
15
28
 
16
- Minimal usage example:
29
+ ```
30
+ db_prefix: "appdump_"
31
+ scp_command: "scp db5:/opt/backups/latest.sql.bz2"
32
+ ```
17
33
 
18
- `db-rotator -p 'appdump_' -c 'scp db5:/opt/backups/latest.sql.bz2'`
34
+ Run: `db-rotator -f /whatever/rotator-config.conf`
19
35
 
20
- Run `db-rotator -h` for the list of configuration options from CLI.
36
+ **Rotate nightly**, so you'll always have a fresh dump during your workday:
21
37
 
22
- You can also set up configuration at `~/.db-rotator.yml`, and omit any CLI options, like so:
38
+ `0 3 * * * bash -lc "db-rotator -f /whatever/rotator-config.conf >> /some/log/file"`
23
39
 
24
- ```
25
- db_prefix: "appdump_"
26
- scp_command: "scp db5:/opt/backups/latest.sql.bz2"
27
- ```
40
+ ## Configuration
28
41
 
29
- 3. Put `db-rotator` in your crontab, and execute it when you're sure your nightly dump has finished.
42
+ Run `db-rotator` without any options to show config options.
30
43
 
31
- `0 3 * * * db-rotator -p 'appdump_' -c 'scp db5:/opt/backups/latest.sql.bz2' >> /some/log/file`
44
+ ### Required
45
+ #### db_prefix (-p)
46
+ Database naming prefix that will apply to all dumps rotated with DBRotator.
47
+ Example: `myproject_`, which might name a DB as myproject_09182013.
32
48
 
33
- ## Required Configuration
34
- - **db_prefix** (-p). Database naming prefix. Example: `myproject_`, which might name a DB as myproject_09182013.
35
- - **scp_command** (-c). Receives second arg of dump path. Example: `scp hostname:/path/to/mysql/backups/backup_filename.sql.bz2`
49
+ #### scp_command (-c)
50
+ How DBRotator retrieves your dumps. This ideally is an scp command, but really can be any command that receives a second argument of the dump destination.
51
+ Example: `scp hostname:/path/to/mysql/backups/backup_filename.sql.bz2`
36
52
 
37
- ## Optional Configuration
53
+ ### Optional
38
54
 
39
- - **local_dump_destination** (-d). Where to put the dump, as a directory. Won't be deleted after running rotator. Default: `/tmp`
55
+ - **local_dump_destination** (-d). Where to put the dump, as a directory. The dump won't be deleted after running rotator. Default: `/tmp`
40
56
  - **mysql_command** (-m). Used for all database management operations. Default: `mysql`
41
57
  - **maximum_dbs** (-n). Maximum number of DBs to maintain, or null to disable pruning. Default: 2
42
58
  - **unarchive_command** (-u). How to unarchive your dump to standard output. Default: `bzip2 -cd`
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'rake/testtask'
2
+ Rake::TestTask.new(:test)
data/bin/db-rotator CHANGED
@@ -5,6 +5,5 @@ require_relative '../lib/db_rotator.rb'
5
5
 
6
6
  config = DBRotatorConfig.new
7
7
  config.configure
8
- puts config.config
9
8
  rotator = DBRotator.new(config)
10
9
  rotator.rotate
data/db-rotator.gemspec CHANGED
@@ -1,13 +1,13 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "db-rotator"
3
- s.version = "0.0.1"
3
+ s.version = "0.0.2"
4
4
  s.summary = "Easy MySQL database rotation and pruning"
5
5
  s.description = "Easy MySQL database rotation and pruning"
6
6
  s.authors = ["Michael Nelson"]
7
7
  s.email = "michael@nelsonware.com"
8
8
  s.executables << "db-rotator"
9
9
  s.files = `git ls-files`.split
10
- #s.test_files = `git ls-files -- test/*`.split
10
+ s.test_files = `git ls-files -- test/*`.split
11
11
  s.homepage = "https://github.com/mcnelson/db-rotator"
12
12
  s.license = "LGPL-2.0"
13
13
 
data/lib/db_rotator.rb CHANGED
@@ -63,7 +63,7 @@ class DBRotator
63
63
 
64
64
  def grant_access
65
65
  verbose_message "Granting ..."
66
- mysql_exec "GRANT ALL ON #{todays_dbname}.* to `%`@`localhost`"
66
+ mysql_exec "GRANT ALL ON #{todays_dbname}.* to #{current_user}"
67
67
  end
68
68
 
69
69
  def populate_schemas
@@ -120,6 +120,10 @@ class DBRotator
120
120
  mysql_exec "SHOW DATABASES"
121
121
  end
122
122
 
123
+ def current_user
124
+ mysql_exec("SELECT current_user()").split("\n").last
125
+ end
126
+
123
127
  def bash_exec(cmd, skip_raise = false)
124
128
  out = `#{cmd} || echo '__failed'`
125
129
  out = out.strip == "__failed" ? nil : out
@@ -8,26 +8,28 @@ class DBRotatorConfig
8
8
  FILE = ".db-rotator.yml"
9
9
 
10
10
  CONFIG = {
11
- db_prefix: [['-p', "--db-prefix PREFIX", "Database naming prefix"], nil],
12
- scp_command: [['-c', "--scp-command COMMAND", "Command to retrive dump. Receives second arg of dump path"], nil],
11
+ db_prefix: [['-p', "--db-prefix PREFIX", "Database naming prefix."], nil],
12
+ scp_command: [['-c', "--scp-command COMMAND", "Command to retrive dump. Receives second arg of dump path."], nil],
13
13
 
14
- local_dump_destination: [['-d', "--local-destination [PATH]", "Where to put the dump, as a directory. Won't be deleted after running rotator.", "Default: /tmp"], "/tmp"],
15
- mysql_command: [['-m', "--mysql-command [COMMAND]", "Used for all database management operations.", "mysql"], "mysql"],
14
+ local_dump_destination: [['-d', "--local-destination [PATH]", "Where to put the dump, as a directory. The dump won't be deleted after running rotator.", "Default: /tmp"], "/tmp"],
15
+ mysql_command: [['-m', "--mysql-command [COMMAND]", "Used for all database management operations.", "Default: mysql"], "mysql"],
16
16
  maximum_dbs: [['-n', "--maximum-dbs [N]", "Maximum number of DBs to maintain, or null to disable pruning.", "Default: 2"], 2],
17
17
  unarchive_command: [['-u', "--unarchive-command [COMMAND]", "How to unarchive your dump to standard output.", "Default: bzip2 -cd"], "bzip2 -cd"],
18
18
  unarchive_extra_pipe: [['-i', "--extra-pipes [SCRIPT1,SCRIPT2]", Array, "Any extra script(s) you want to run between unarchive & import.", "Example: -i 'script1,script2'"], nil],
19
- reasonable_diskspace: [['-s', "--minimum-diskspace [GB]", "Rough estimate of temporary disk space required to import a typical dump, in GB.", "Default: nil"], nil],
19
+ reasonable_diskspace: [['-s', "--minimum-diskspace [GB]", "Rough estimate of temporary disk space required to import a typical dump, in GB."], nil],
20
20
  rails_db_yaml_path: [['-y', "--rails-db-yaml [PATH]", "Updates database name in your YAML file when given.", "Default: nil"], nil],
21
- rails_environments: [['-e', "--rails-db-environments [ENV1,ENV2]", Array, "In conjunction with -y, which rails envs to update DB name for.", "Default [development]"], ["development"]],
21
+ rails_environments: [['-e', "--rails-db-environments [ENV1,ENV2]", Array, "In conjunction with -y, which rails envs to update DB name for. Default: development"], ["development"]],
22
22
 
23
- config_file: [['-f', "--config-file PATH", "Load configuration from .yml file"], nil],
23
+ config_file: [['-f', "--config-file PATH", "Runs rotator with configuration from this .yml file."], nil],
24
24
  }
25
25
 
26
26
  REQUIRED = %i(db_prefix scp_command)
27
+ EXCLUDE_FROM_GENERATE_FILE = %i(config_file dump_filename)
27
28
  attr_reader :config
28
29
 
29
30
  def initialize
30
31
  @config = {}
32
+ @generate_output_file = nil
31
33
  end
32
34
 
33
35
  def configure
@@ -41,6 +43,12 @@ class DBRotatorConfig
41
43
  check_required
42
44
  add_default_values
43
45
  add_derived_values
46
+
47
+ if @generate_output_file
48
+ generate_config
49
+ exit
50
+ end
51
+
44
52
  rescue FileNotFoundError => e
45
53
  puts "There was a problem loading configuration: #{e.message}"
46
54
  puts cli_parser.summarize
@@ -78,6 +86,10 @@ class DBRotatorConfig
78
86
  end
79
87
  end
80
88
 
89
+ opts.on("-g", "--generate-config FILE", "Generates .yml config file from given arguments.") do |v|
90
+ @generate_output_file = v
91
+ end
92
+
81
93
  opts.on("-v", "--verbose") do |v|
82
94
  @config[:verbose] = v
83
95
  end
@@ -97,6 +109,15 @@ class DBRotatorConfig
97
109
  REQUIRED.each { |k| raise "config option '#{k.to_s} is required'" if @config[k].nil? }
98
110
  end
99
111
 
112
+ def generate_config
113
+ File.write(@generate_output_file, config_yaml)
114
+ end
115
+
116
+ def config_yaml
117
+ require 'pry'; binding.pry
118
+ @config.reject { |k, _| EXCLUDE_FROM_GENERATE_FILE.include?(k) } .to_yaml
119
+ end
120
+
100
121
  def add_default_values
101
122
  CONFIG.each do |key, pair|
102
123
  @config[key] ||= pair.last
@@ -105,7 +126,13 @@ class DBRotatorConfig
105
126
 
106
127
  def add_derived_values
107
128
  # Figure out the dump filename
108
- pn = Pathname.new(@config[:scp_command])
129
+ cmd = if @config[:scp_command].strip.match(/^scp/)
130
+ @config[:scp_command].split(':').last
131
+ else
132
+ @config[:scp_command]
133
+ end
134
+
135
+ pn = Pathname.new(cmd)
109
136
  @config[:dump_filename] = pn.basename.to_s
110
137
  end
111
138
  end
@@ -0,0 +1,54 @@
1
+ -- MySQL dump 10.13 Distrib 5.5.34, for Linux (x86_64)
2
+ --
3
+ -- Host: localhost Database: whatever
4
+ -- ------------------------------------------------------
5
+ -- Server version 5.5.34-32.0-log
6
+
7
+ /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8
+ /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9
+ /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
10
+ /*!40101 SET NAMES utf8 */;
11
+ /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12
+ /*!40103 SET TIME_ZONE='+00:00' */;
13
+ /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14
+ /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15
+ /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16
+ /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17
+
18
+ --
19
+ -- Table structure for table `test`
20
+ --
21
+
22
+ DROP TABLE IF EXISTS `test`;
23
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
24
+ /*!40101 SET character_set_client = utf8 */;
25
+ CREATE TABLE `test` (
26
+ `id` int(11) NOT NULL AUTO_INCREMENT,
27
+ `column_varchar` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
28
+ `column_int` tinyint(1) DEFAULT NULL,
29
+ `column_date` datetime DEFAULT NULL,
30
+ `column_nonnull` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
31
+ PRIMARY KEY (`id`)
32
+ ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
33
+ /*!40101 SET character_set_client = @saved_cs_client */;
34
+
35
+ --
36
+ -- Dumping data for table `test`
37
+ --
38
+
39
+ LOCK TABLES `test` WRITE;
40
+ /*!40000 ALTER TABLE `test` DISABLE KEYS */;
41
+ INSERT INTO `test` VALUES (1,'Herpa Derpa',1,'2013-01-01 01:10:20','Blargh'),(2,'Dorp Schlorp',0,'2013-09-18 04:50:12','Rawr');
42
+ /*!40000 ALTER TABLE `test` ENABLE KEYS */;
43
+ UNLOCK TABLES;
44
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
45
+
46
+ /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
47
+ /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
48
+ /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
49
+ /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
50
+ /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
51
+ /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
52
+ /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
53
+
54
+ -- Dump completed on 2013-11-20 9:00:47
Binary file
Binary file
Binary file
data/test/helper.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../lib/db_rotator'
3
+ require_relative '../lib/db_rotator_config'
4
+
5
+ TEST_SCHEMA_PREFIX = "__minitest_"
6
+
7
+ def dummy_config(config={})
8
+ DBRotatorConfig.new.tap do |cfg|
9
+ cfg.instance_variable_set(:@config, {
10
+ db_prefix: TEST_SCHEMA_PREFIX,
11
+ scp_command: "cp test/fixtures/basic_dump.sql.bz2",
12
+
13
+ local_dump_destination: "/tmp"
14
+ }.merge(config))
15
+
16
+ cfg.add_default_values
17
+ cfg.add_derived_values
18
+ end
19
+ end
@@ -1,6 +1,45 @@
1
-
2
- require 'minitest'
1
+ require_relative '../test/helper'
3
2
 
4
3
  describe DBRotator do
4
+ after do
5
+ `mysql -B -e 'SHOW SCHEMAS;'`.split.each do |schema|
6
+ if /^#{TEST_SCHEMA_PREFIX}/.match(schema)
7
+ `mysql -e "DROP SCHEMA #{schema}"`
8
+ end
9
+ end
10
+ end
11
+
12
+ def dbname_with(time)
13
+ "__minitest_#{ time.strftime(DBRotator::TIME_FORMAT) }"
14
+ end
15
+
16
+ describe "integration test" do
17
+ it "rotates databases correctly" do
18
+ dbr = DBRotator.new(dummy_config)
19
+
20
+ t1 = Time.new(2013, 1, 1)
21
+ t2 = Time.new(2013, 1, 2)
22
+ t3 = Time.new(2013, 1, 3)
23
+
24
+ Time.stub(:now, t1) do
25
+ dbr.rotate
26
+ `mysql -B -e 'SHOW SCHEMAS;'`.must_include(dbname_with(t1))
27
+ `mysql -B -e 'SHOW TABLES FROM #{dbname_with(t1)};'`.must_include("test")
28
+ end
29
+
30
+ Time.stub(:now, t2) do
31
+ dbr.rotate
32
+ `mysql -B -e 'SHOW SCHEMAS;'`.must_include(dbname_with(t2))
33
+ `mysql -B -e 'SHOW SCHEMAS;'`.must_include(dbname_with(t1))
34
+ end
35
+
36
+ Time.stub(:now, t3) do
37
+ dbr.rotate
38
+ `mysql -B -e 'SHOW SCHEMAS;'`.must_include(dbname_with(t3))
39
+ `mysql -B -e 'SHOW SCHEMAS;'`.must_include(dbname_with(t2))
5
40
 
41
+ `mysql -B -e 'SHOW SCHEMAS;'`.wont_include(dbname_with(t1))
42
+ end
43
+ end
44
+ end
6
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db-rotator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Nelson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-17 00:00:00.000000000 Z
11
+ date: 2013-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -34,10 +34,16 @@ files:
34
34
  - .gitignore
35
35
  - LICENSE
36
36
  - README.md
37
+ - Rakefile
37
38
  - bin/db-rotator
38
39
  - db-rotator.gemspec
39
40
  - lib/db_rotator.rb
40
41
  - lib/db_rotator_config.rb
42
+ - test/fixtures/basic_dump.sql
43
+ - test/fixtures/basic_dump.sql.bz2
44
+ - test/fixtures/basic_dump.sql.gz
45
+ - test/fixtures/basic_dump.sql.zip
46
+ - test/helper.rb
41
47
  - test/test_db_rotator.rb
42
48
  homepage: https://github.com/mcnelson/db-rotator
43
49
  licenses:
@@ -63,4 +69,10 @@ rubygems_version: 2.0.2
63
69
  signing_key:
64
70
  specification_version: 4
65
71
  summary: Easy MySQL database rotation and pruning
66
- test_files: []
72
+ test_files:
73
+ - test/fixtures/basic_dump.sql
74
+ - test/fixtures/basic_dump.sql.bz2
75
+ - test/fixtures/basic_dump.sql.gz
76
+ - test/fixtures/basic_dump.sql.zip
77
+ - test/helper.rb
78
+ - test/test_db_rotator.rb