cassandra_backup 0.1.1 → 0.2.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.
- data/cassandra_backup.gemspec +1 -1
- data/lib/cassandra_backup/command.rb +7 -1
- data/lib/cassandra_backup/config.rb +13 -28
- data/lib/cassandra_backup/dumper.rb +25 -10
- data/test/cassandra_backup/command_test.rb +14 -1
- data/test/cassandra_backup/config_test.rb +3 -10
- data/test/cassandra_backup/dumper_test.rb +54 -19
- metadata +2 -2
data/cassandra_backup.gemspec
CHANGED
@@ -27,6 +27,12 @@ module CassandraBackup
|
|
27
27
|
opts.on('-w', '--where=\'where_conditions\'') do |v|
|
28
28
|
options[:where] = v
|
29
29
|
end
|
30
|
+
opts.on('--[no-]create-info', "Write out CREATE statements. Defaults to true.") do |v|
|
31
|
+
options[:create_info] = v
|
32
|
+
end
|
33
|
+
opts.on('--[no-]data', "Write out table data. Defaults to true.") do |v|
|
34
|
+
options[:data] = v
|
35
|
+
end
|
30
36
|
opts.on('-h', 'Show this help message.') do
|
31
37
|
$stdout.puts opts; exit
|
32
38
|
end
|
@@ -44,4 +50,4 @@ module CassandraBackup
|
|
44
50
|
(options[name] ||= []).concat value.split(/\s|,/)
|
45
51
|
end
|
46
52
|
end
|
47
|
-
end
|
53
|
+
end
|
@@ -1,46 +1,31 @@
|
|
1
1
|
module CassandraBackup
|
2
2
|
class Config
|
3
|
+
DEFAULT_OPTIONS = {
|
4
|
+
input: STDIN,
|
5
|
+
output: STDOUT,
|
6
|
+
data: true,
|
7
|
+
create_info: true,
|
8
|
+
servers: ['127.0.0.1:9160']
|
9
|
+
}
|
10
|
+
|
3
11
|
attr_reader :options
|
4
12
|
def initialize(options = {})
|
5
|
-
@options = options
|
6
|
-
|
13
|
+
@options = DEFAULT_OPTIONS.dup.update(options)
|
7
14
|
require_version!
|
8
15
|
end
|
9
16
|
|
10
|
-
|
11
|
-
|
12
|
-
options[
|
13
|
-
else
|
14
|
-
['127.0.0.1:9160']
|
17
|
+
[:servers, :input, :output, :create_info, :data, :keyspace, :tables, :where].each do |option|
|
18
|
+
define_method option do
|
19
|
+
options[option]
|
15
20
|
end
|
16
21
|
end
|
17
22
|
|
18
|
-
def input
|
19
|
-
options[:input] || STDIN
|
20
|
-
end
|
21
|
-
|
22
|
-
def output
|
23
|
-
options[:output] || STDOUT
|
24
|
-
end
|
25
|
-
|
26
23
|
def require_version!
|
27
24
|
if options[:version]
|
28
25
|
require "cassandra-cql/#{options[:version]}"
|
29
26
|
end
|
30
27
|
end
|
31
28
|
|
32
|
-
def keyspace
|
33
|
-
options[:keyspace]
|
34
|
-
end
|
35
|
-
|
36
|
-
def tables
|
37
|
-
options[:tables]
|
38
|
-
end
|
39
|
-
|
40
|
-
def where
|
41
|
-
options[:where]
|
42
|
-
end
|
43
|
-
|
44
29
|
def connection
|
45
30
|
if keyspace
|
46
31
|
options = {keyspace: keyspace}
|
@@ -50,4 +35,4 @@ module CassandraBackup
|
|
50
35
|
@connection ||= CassandraCQL::Database.new(servers, options)
|
51
36
|
end
|
52
37
|
end
|
53
|
-
end
|
38
|
+
end
|
@@ -4,9 +4,7 @@ module CassandraBackup
|
|
4
4
|
new(command.config).run
|
5
5
|
end
|
6
6
|
|
7
|
-
attr_reader :config
|
8
|
-
attr_reader :cqlsh
|
9
|
-
attr_reader :primary_keys
|
7
|
+
attr_reader :config, :cqlsh, :primary_keys
|
10
8
|
def initialize(config)
|
11
9
|
@config = config
|
12
10
|
@cqlsh = CassandraBackup::Cqlsh.new(config)
|
@@ -15,24 +13,29 @@ module CassandraBackup
|
|
15
13
|
|
16
14
|
def run
|
17
15
|
table_names.each { |table_name| dump_table_schema(table_name) }
|
18
|
-
table_names.each { |table_name| dump_table_rows(table_name) }
|
16
|
+
table_names.each { |table_name| dump_table_rows(table_name) } if config.data
|
19
17
|
end
|
20
18
|
|
21
19
|
def dump_table_schema(table_name)
|
22
20
|
table_description = cqlsh.exec("DESCRIBE COLUMNFAMILY #{table_name}")
|
23
21
|
capture_primary_key(table_name, table_description)
|
24
|
-
|
25
|
-
config.
|
22
|
+
|
23
|
+
if config.create_info
|
24
|
+
config.output.puts(table_description)
|
25
|
+
config.output.puts
|
26
|
+
end
|
26
27
|
end
|
27
28
|
|
28
29
|
def dump_table_rows(table_name)
|
30
|
+
return unless config.data
|
31
|
+
|
29
32
|
primary_key = primary_key_for(table_name)
|
30
33
|
each_row(table_name) do |row|
|
31
34
|
next if row.keys.size == 1 && row.keys.first == primary_key
|
32
35
|
|
33
36
|
colummns = CassandraCQL::Statement.quote(row.keys)
|
34
37
|
values = CassandraCQL::Statement.quote(row.values.map { |v| CassandraCQL::Statement.escape(v) })
|
35
|
-
|
38
|
+
|
36
39
|
config.output.puts "INSERT INTO #{table_name} (#{colummns}) VALUES (#{values});"
|
37
40
|
end
|
38
41
|
end
|
@@ -53,7 +56,7 @@ module CassandraBackup
|
|
53
56
|
def primary_key_for(table_name)
|
54
57
|
primary_keys[table_name] || 'id'
|
55
58
|
end
|
56
|
-
|
59
|
+
|
57
60
|
def capture_primary_key(table_name, table_description)
|
58
61
|
if table_description =~ /^\s*(\w+).*PRIMARY KEY/
|
59
62
|
primary_keys[table_name] = $1
|
@@ -62,7 +65,9 @@ module CassandraBackup
|
|
62
65
|
|
63
66
|
def each_row(table_name)
|
64
67
|
primary_key = primary_key_for(table_name)
|
65
|
-
|
68
|
+
select_cql = "SELECT * FROM #{table_name}"
|
69
|
+
|
70
|
+
cassandra_result = execute_with_where(select_cql, [config.where])
|
66
71
|
|
67
72
|
while cassandra_result.rows > 0
|
68
73
|
last_fetch = nil
|
@@ -72,7 +77,17 @@ module CassandraBackup
|
|
72
77
|
end
|
73
78
|
|
74
79
|
where_cql = "'#{primary_key}' > '#{last_fetch[primary_key]}'"
|
75
|
-
cassandra_result =
|
80
|
+
cassandra_result = execute_with_where(select_cql, [where_cql, config.where])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def execute_with_where(select_cql, wheres)
|
85
|
+
wheres = wheres.compact
|
86
|
+
|
87
|
+
if wheres.any?
|
88
|
+
execute "#{select_cql} WHERE #{wheres.join(' AND ')}"
|
89
|
+
else
|
90
|
+
execute select_cql
|
76
91
|
end
|
77
92
|
end
|
78
93
|
end
|
@@ -13,6 +13,11 @@ class CassandraBackup::CommandTest < MiniTest::Spec
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
def test_boolean_options
|
17
|
+
assert_equal false, command('zombies --no-data').options[:data]
|
18
|
+
assert_equal false, command('zombies --no-create-info').options[:create_info]
|
19
|
+
end
|
20
|
+
|
16
21
|
def test_array_options
|
17
22
|
[:servers, :tables].each do |option|
|
18
23
|
assert_nil command('zombies').options[option]
|
@@ -22,6 +27,14 @@ class CassandraBackup::CommandTest < MiniTest::Spec
|
|
22
27
|
end
|
23
28
|
end
|
24
29
|
|
30
|
+
def test_boolean_options
|
31
|
+
[ { "create-info" => :create_info }, { "data" => :data }].each do |option|
|
32
|
+
assert_nil command('zombies').options[option.values.first]
|
33
|
+
assert command("zombies --#{option.keys.first}").options[option.values.first], "--#{option} should enable command"
|
34
|
+
refute command("zombies --no-#{option.keys.first}").options[option.values.first], "--no-#{option} should disable command"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
25
38
|
def test_config
|
26
39
|
assert_kind_of CassandraBackup::Config, command('zombies').config
|
27
40
|
end
|
@@ -30,4 +43,4 @@ class CassandraBackup::CommandTest < MiniTest::Spec
|
|
30
43
|
def command(line)
|
31
44
|
CassandraBackup::Command.new Shellwords.shellsplit(line)
|
32
45
|
end
|
33
|
-
end
|
46
|
+
end
|
@@ -1,19 +1,12 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class CassandraBackup::ConfigTest < MiniTest::Spec
|
4
|
-
def
|
4
|
+
def test_defaults
|
5
5
|
assert_equal ['127.0.0.1:9160'], config.servers
|
6
|
-
assert_equal ['foo'], config(servers: ['foo']).servers
|
7
|
-
end
|
8
|
-
|
9
|
-
def test_input
|
10
6
|
assert_equal STDIN, config.input
|
11
|
-
refute_equal STDIN, config(input: StringIO.new).input
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_output
|
15
7
|
assert_equal STDOUT, config.output
|
16
|
-
|
8
|
+
assert_equal true, config.data
|
9
|
+
assert_equal true, config.create_info
|
17
10
|
end
|
18
11
|
|
19
12
|
def test_require_version
|
@@ -6,38 +6,73 @@ class CassandraBackup::DumperTest < MiniTest::Spec
|
|
6
6
|
assert_equal ['presidents'], create_dumper(tables: ['presidents']).table_names
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
|
11
|
-
dumper = create_dumper output: output
|
9
|
+
def test_dump_create_info
|
10
|
+
dumper = create_dumper tables: ['senators']
|
12
11
|
|
13
|
-
dumper
|
12
|
+
output = run_dumper(dumper)
|
14
13
|
|
15
|
-
output
|
16
|
-
lines = output.readlines
|
17
|
-
assert_match /CREATE TABLE senators/, lines.first
|
18
|
-
assert_equal "\n", lines.last
|
14
|
+
assert_match /CREATE TABLE senators/, output
|
19
15
|
assert_equal({"senators" => "id"}, dumper.primary_keys)
|
20
16
|
end
|
21
17
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
18
|
+
def test_bypass_create_info
|
19
|
+
dumper = create_dumper create_info: false, tables: ['senators']
|
20
|
+
|
21
|
+
output = run_dumper(dumper)
|
22
|
+
|
23
|
+
refute_match /CREATE TABLE/, output
|
24
|
+
assert_equal({"senators" => "id"}, dumper.primary_keys)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_dump_data
|
28
|
+
dumper = create_dumper
|
25
29
|
dumper.execute "TRUNCATE senators"
|
26
30
|
dumper.execute "INSERT INTO senators ('id', 'name', 'state') VALUES ('abc', 'K''la', 'WA')"
|
27
31
|
dumper.execute "INSERT INTO senators ('id', 'name', 'state') VALUES ('xyz', 'Bob', 'NY')"
|
28
32
|
|
29
|
-
dumper
|
33
|
+
output = run_dumper(dumper)
|
30
34
|
|
31
|
-
output.
|
32
|
-
|
35
|
+
assert output.include?("INSERT INTO senators ('id','name','state') VALUES ('xyz','Bob','NY');")
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_dump_data_with_where
|
39
|
+
dumper = create_dumper where: "'state' = 'WA'", tables: ['senators']
|
40
|
+
dumper.execute "TRUNCATE senators"
|
41
|
+
dumper.execute "INSERT INTO senators ('id', 'name', 'state') VALUES ('abc', 'Patty Murray', 'WA')"
|
42
|
+
dumper.execute "INSERT INTO senators ('id', 'name', 'state') VALUES ('xyz', 'John McCain', 'AZ')"
|
43
|
+
|
44
|
+
output = run_dumper(dumper)
|
45
|
+
|
46
|
+
assert_match /Patty Murray/, output
|
47
|
+
refute_match /John McCain/, output
|
48
|
+
end
|
33
49
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
50
|
+
def test_bypass_data
|
51
|
+
dumper = create_dumper data: false, tables: ['senators']
|
52
|
+
|
53
|
+
dumper.execute "TRUNCATE senators"
|
54
|
+
dumper.execute "INSERT INTO senators ('id', 'name', 'state') VALUES ('abc', 'K''la', 'WA')"
|
55
|
+
|
56
|
+
output = run_dumper(dumper)
|
57
|
+
|
58
|
+
assert_match /CREATE TABLE senators/, output
|
59
|
+
refute_match /INSERT INTO senators/, output
|
60
|
+
end
|
38
61
|
|
39
62
|
private
|
63
|
+
def config(options = {})
|
64
|
+
CassandraBackup::Config.new(options.merge(keyspace: 'cassandra_dump_test'))
|
65
|
+
end
|
66
|
+
|
40
67
|
def create_dumper(options = {})
|
41
|
-
CassandraBackup::Dumper.new
|
68
|
+
CassandraBackup::Dumper.new config(options)
|
69
|
+
end
|
70
|
+
|
71
|
+
def run_dumper(dumper)
|
72
|
+
output = StringIO.new
|
73
|
+
dumper.config.options[:output] = output
|
74
|
+
dumper.run
|
75
|
+
output.rewind
|
76
|
+
output.readlines.to_s
|
42
77
|
end
|
43
78
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cassandra_backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: cassandra-cql
|