braavos 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ class Braavos::Storage::File < Braavos::Storage::StorageBase
2
+
3
+ def load_file(location)
4
+ Braavos.logger.debug("load_file: #{location}")
5
+ File.read(location)
6
+ end
7
+
8
+ # Returns a recursive listing of {"name" => true} if directory and {"name" => *file size*} if file
9
+ def list_dir(location)
10
+ Braavos.logger.debug("list_dir: #{location}")
11
+ listing = Dir["#{location}/**/*"].inject({}) do |h,i|
12
+ h[i] = File.size?(i) if not File.directory?(i)
13
+ h
14
+ end
15
+ Braavos.logger.debug("\t#{listing}")
16
+ listing
17
+ end
18
+
19
+ def clear_result(location)
20
+ FileUtils.rm_f File.join(location, "_FAILED")
21
+ end
22
+
23
+ def has_success?(location)
24
+ path = File.join(location, "_COMPLETED")
25
+ list_dir(location).size > 0 &&
26
+ File.exists?(path)
27
+ end
28
+
29
+ def script_path(location)
30
+ @mkdir_cache ||= Hash.new do |h, key|
31
+ FileUtils.mkdir_p(key)
32
+ h[key] = true
33
+ end
34
+
35
+ @mkdir_cache[File.dirname(location)]
36
+ location
37
+ end
38
+
39
+ def write_file(location, contents)
40
+ script_path(location) # for mkdir -p
41
+ if contents.is_a?(Hash) && contents.include?(:file)
42
+ FileUtils.cp(contents[:file], location)
43
+ else
44
+ File.open(location, 'w') do |f| f.write contents end
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,41 @@
1
+ class Braavos::Storage::S3 < Braavos::Storage::StorageBase
2
+
3
+ attr_reader :s3
4
+
5
+ def initialize
6
+ @s3 = AWS::S3.new
7
+ end
8
+
9
+ def load_file(location)
10
+ s3.buckets[Braavos.config.bucket_name].objects[location].read
11
+ end
12
+
13
+ def list_dir(location)
14
+ Braavos.logger.debug("S3 list_dir: #{Braavos.config.bucket_name}/#{location}")
15
+ listing = s3.buckets[Braavos.config.bucket_name].objects.with_prefix("#{location}/").inject({}) do |h,i|
16
+ h[i.key] = 0 if not i.key.end_with?('_$folder$') # disable i.content_length due to performance
17
+ h
18
+ end
19
+ Braavos.logger.debug("\t#{listing}")
20
+ listing
21
+ end
22
+
23
+ def has_success?(location)
24
+ path = File.join(location, "_COMPLETED")
25
+ list_dir(location).size > 0 &&
26
+ s3.buckets[Braavos.config.bucket_name].objects[path].exists?
27
+ end
28
+
29
+ def clear_result(location)
30
+ path = File.join(location, "_FAILED")
31
+ s3.buckets[Braavos.config.bucket_name].objects[path].delete
32
+ end
33
+
34
+ def script_path(location)
35
+ File.join(Braavos.config.bucket_name, location)
36
+ end
37
+
38
+ def write_file(location, contents)
39
+ s3.buckets[Braavos.config.bucket_name].objects[location].write(contents)
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ class Braavos::Storage::StorageBase
2
+
3
+
4
+ def get_cluster
5
+ @cluster ||= JSON.parse(load_file(File.join(Braavos.config.backup_path, 'cluster.json')))
6
+ end
7
+
8
+ def verify_cluster(write_mode)
9
+ cluster = get_cluster
10
+ if write_mode
11
+ if Braavos.config.name != cluster['name']
12
+ raise "Mismatching name (expecting: #{Braavos.config.name}, found: #{cluster['name']})"
13
+ end
14
+ if Braavos.config.environment != cluster['environment']
15
+ raise "Mismatching environment (expecting: #{Braavos.config.environment}, found: #{cluster['environment']})"
16
+ end
17
+ end
18
+ end
19
+
20
+ def find_node_id(force_node_id=nil)
21
+ @node_id = force_node_id if force_node_id
22
+ @node_id ||= -> do
23
+ cluster = get_cluster
24
+ instance_id = Braavos::Service.instance_id
25
+ cluster['nodes'].each do |node_id, node_info|
26
+ if node_info['instance_id'] == instance_id
27
+ return node_id
28
+ end
29
+ end
30
+ raise "Node not found: #{instance_id}"
31
+ end.call
32
+ end
33
+
34
+ # TODO: option for --completed --pending, maybe return size here
35
+ def list_backups(node_id)
36
+ result = {full: [], incr: []}
37
+ listing = list_dir(Braavos.config.backup_path)
38
+ listing.each do |l, z|
39
+ if match = l.match(/\/(full|incr)\/(\w+)\/#{node_id}\/_COMPLETED\Z/)
40
+ path, name = match.captures
41
+ result[path.to_sym] << name
42
+ end
43
+ end
44
+ result
45
+ end
46
+
47
+
48
+ end
@@ -0,0 +1,49 @@
1
+ require 'erb'
2
+
3
+ class Braavos::Template
4
+
5
+ attr_accessor :template_paths
6
+
7
+ def initialize(template_paths)
8
+ @template_paths = template_paths
9
+ end
10
+
11
+ def load_template(template, params={})
12
+ content = find_template(template)
13
+ raise "Unable to find template: #{template} in path: #{template_paths}" unless content
14
+ result_template(content, params)
15
+ end
16
+
17
+ def result_template(template, params)
18
+ ns = Namespace.new(params.merge(config: Braavos.config))
19
+ ERB.new(template).result(ns.get_binding)
20
+ end
21
+
22
+ private
23
+
24
+ # http://stackoverflow.com/questions/1338960/ruby-templates-how-to-pass-variables-into-inlined-erb
25
+ class Namespace
26
+ def initialize(hash)
27
+ hash.each do |key, value|
28
+ singleton_class.send(:define_method, key) { value }
29
+ end
30
+ end
31
+
32
+ def get_binding
33
+ binding
34
+ end
35
+ end
36
+
37
+ def find_template(template)
38
+ content = nil
39
+ template_paths.each do |path|
40
+ file_path = File.join(path, template)
41
+ if File.exists?(file_path)
42
+ content = File.read(file_path)
43
+ break
44
+ end
45
+ end
46
+ content
47
+ end
48
+
49
+ end
@@ -0,0 +1,3 @@
1
+ class Braavos
2
+ VERSION = "0.0.7"
3
+ end
@@ -0,0 +1 @@
1
+ exec nodetool clearsnapshot $1 -t $2
@@ -0,0 +1 @@
1
+ exec nodetool snapshot $1 -t $2
@@ -0,0 +1,4 @@
1
+ KEYSPACE=$1
2
+ OUTPUT_LOC=$2
3
+
4
+ echo "describe keyspace $KEYSPACE" | cqlsh <%= local_host_ip %> > $OUTPUT_LOC/$KEYSPACE.cql
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ DEST_DIR=$1
6
+ shift
7
+ DATA_DIR=$1
8
+ shift
9
+
10
+ cd ${DATA_DIR} && tar zcf ${DEST_DIR}/system.tgz $*
11
+
12
+ nodetool ring > ${DEST_DIR}/ring.txt
13
+ nodetool info > ${DEST_DIR}/info.txt
14
+ nodetool cfstats > ${DEST_DIR}/cfstats.txt
15
+ nodetool status > ${DEST_DIR}/status.txt
16
+ nodetool compactionstats > ${DEST_DIR}/compactionstats.txt
17
+ nodetool netstats > ${DEST_DIR}/netstats.txt
18
+ nodetool gossipinfo > ${DEST_DIR}/gossipinfo.txt
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ FILE_LOC=$1
6
+ DEST_LOC=$2
7
+ PROCESS_LOC=${FILE_LOC}
8
+
9
+ <% if config.storage_backing == 's3' %>
10
+
11
+ PROCESS_LOC=<%= config.temp_loc %>/braavos-cassdown-$$.${RANDOM}.tgz
12
+
13
+ function getTimeoutCmd() {
14
+ if hash timeout &> /dev/null
15
+ then
16
+ echo timeout
17
+ else
18
+ echo gtimeout
19
+ fi
20
+ }
21
+
22
+ trap "echo Caught kill for ${FILE_LOC}; rm ${PROCESS_LOC}; exit 1" SIGHUP SIGINT SIGTERM
23
+
24
+ # Install GNU coreutils for `timeout`
25
+ # s3cmd can hang indefinitely due to network constraints
26
+ # So kill after timeout of 2 minutes, an ideal timeout when run on EC2
27
+ $(getTimeoutCmd) 120 s3cmd get s3://${FILE_LOC} ${PROCESS_LOC}
28
+ if [ $? -eq 124 ]; then
29
+ # Timeout occurred, try one more time.
30
+ echo Timed Out! Retrying download of ${FILE_LOC}
31
+ s3cmd get s3://${FILE_LOC} ${PROCESS_LOC}
32
+ fi
33
+
34
+ <% end %>
35
+
36
+ echo Expanding ${FILE_LOC} to ${DEST_LOC}
37
+ mkdir -p ${DEST_LOC}
38
+ zcat -q ${PROCESS_LOC} | tar -xC ${DEST_LOC}
39
+
40
+ <% if config.storage_backing == 's3' %>
41
+
42
+ rm ${PROCESS_LOC}
43
+
44
+ <% end %>
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ FILE_LOC=$1
6
+ START_LOC=$(pwd)
7
+ DEST_LOC=$2
8
+ TMP_FILE=<%= config.temp_loc %>/braavos-cassup-$$.${RANDOM}.tgz
9
+
10
+ trap "echo Caught kill for ${FILE_LOC}; rm ${TMP_FILE}; exit 1" SIGHUP SIGINT SIGTERM
11
+
12
+ BASE=$(dirname $FILE_LOC)
13
+ FILE_PART=$(basename $FILE_LOC)
14
+
15
+ echo Processing $FILE_PART in $BASE to ${DEST_LOC}
16
+ cd $BASE && tar zcf $TMP_FILE ${FILE_PART}-*
17
+
18
+ <% if config.storage_backing == 'file' %>
19
+
20
+ mv $TMP_FILE $START_LOC/$DEST_LOC
21
+
22
+ <% elsif config.storage_backing == 's3' %>
23
+
24
+ s3cmd put ${TMP_FILE} s3://${DEST_LOC}
25
+ rm ${TMP_FILE}
26
+
27
+ <% end %>
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+
3
+ class Braavos::CommandTest < MiniTest::Spec
4
+ attr_accessor :command, :file
5
+
6
+ before do
7
+ Braavos.logger.level = Logger::WARN
8
+ @file = Tempfile.new "braavos_command_test"
9
+ @command = Braavos::Command.new
10
+ end
11
+
12
+ after do
13
+ file.close
14
+ file.unlink
15
+ end
16
+
17
+ def test_simple
18
+ command.execute("echo $* >> #{file.path}", ['a', 'b', 'c'])
19
+ result = file.read
20
+ assert_equal "a b c\n", result
21
+ end
22
+
23
+ def test_arguments
24
+ command.execute("echo $1 >> #{file.path}", ['a', 'b', 'c'])
25
+ result = file.read
26
+ assert_equal "a\n", result
27
+ end
28
+
29
+ def test_system
30
+ command.execute("echo $* >> #{file.path}", ['a', 'b', 'c'])
31
+ result = file.read
32
+ assert result
33
+ end
34
+ end
@@ -0,0 +1,66 @@
1
+ require 'test_helper'
2
+
3
+ class Braavos::ConfigTest < MiniTest::Spec
4
+
5
+ def default_config
6
+ {temp_loc: '/tmp'}
7
+ end
8
+
9
+ def test_regular_backup_path
10
+ c = default_config.merge(name: 'neville', environment: 'env', service: 'cassandra')
11
+ config = Braavos::Config.new(c)
12
+
13
+ assert_equal "neville/env/cassandra", config.backup_path
14
+ end
15
+
16
+ def test_prefixed_backup_path
17
+ c = default_config.merge(name: 'neville', environment: 'env', service: 'cassandra', backup_prefix: '/Users/nevillek/backup/')
18
+ config = Braavos::Config.new(c)
19
+
20
+ assert_equal "/Users/nevillek/backup/neville/env/cassandra", config.backup_path
21
+
22
+ c = default_config.merge(name: 'neville', environment: 'env', service: 'cassandra', backup_prefix: 'Users/nevillek/backup')
23
+ config = Braavos::Config.new(c)
24
+
25
+ assert_equal "Users/nevillek/backup/neville/env/cassandra", config.backup_path
26
+
27
+ c = default_config.merge(name: 'neville', environment: 'env', service: 'cassandra', backup_prefix: '')
28
+ config = Braavos::Config.new(c)
29
+
30
+ assert_equal "neville/env/cassandra", config.backup_path
31
+ end
32
+
33
+ def test_service_cassandra
34
+ c = default_config.merge(name: 'neville', environment: 'env', service: 'cassandra')
35
+ config = Braavos::Config.new(c)
36
+
37
+ assert config.service_class == Braavos::Service::Cassandra
38
+ end
39
+
40
+ def test_service_es
41
+ c = default_config.merge(name: 'neville', environment: 'env', service: 'elasticsearch')
42
+ config = Braavos::Config.new(c)
43
+
44
+ assert config.service_class == Braavos::Service::Elasticsearch
45
+ end
46
+
47
+ def test_storage_s3
48
+ c = default_config.merge(name: 'neville', environment: 'env', service: 'cassandra')
49
+ config = Braavos::Config.new(c)
50
+
51
+ assert config.storage_class == Braavos::Storage::S3
52
+
53
+ c = default_config.merge(name: 'neville', environment: 'env', service: 'cassandra', storage_backing: 's3')
54
+ config = Braavos::Config.new(c)
55
+
56
+ assert config.storage_class == Braavos::Storage::S3
57
+ end
58
+
59
+ def test_storage_file
60
+ c = default_config.merge(name: 'neville', environment: 'env', service: 'cassandra', storage_backing: 'file')
61
+ config = Braavos::Config.new(c)
62
+
63
+ assert config.storage_class == Braavos::Storage::File
64
+ end
65
+
66
+ end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ class Braavos::ParallelTest < MiniTest::Spec
4
+ attr_accessor :parallel, :file
5
+
6
+ before do
7
+ Braavos.logger.level = Logger::WARN
8
+ @file = Tempfile.new "braavos_parallel_test"
9
+ @parallel = Braavos::Parallel.new
10
+ end
11
+
12
+ after do
13
+ file.close
14
+ file.unlink
15
+ end
16
+
17
+ def test_simple
18
+ result = parallel.execute("echo $1 >> #{file.path}", ['a', 'b', 'c'])
19
+ result = file.read
20
+ assert_equal ['a', 'b', 'c'], result.lines.map(&:chomp).sort
21
+ end
22
+
23
+ def test_multiple_inputs
24
+ parallel.execute("echo $* >> #{file.path}", [['a', 'apple'], ['b', 'ball'], ['c', 'crawl']])
25
+ result = file.read
26
+ assert_equal ['a apple', 'b ball', 'c crawl'], result.lines.map(&:chomp).sort
27
+ end
28
+
29
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ class Braavos::TemplateTest < MiniTest::Spec
4
+ attr_accessor :template
5
+
6
+ before do
7
+ @template = Braavos::Template.new(['test/template'])
8
+ end
9
+
10
+ def test_result_simple
11
+ result = template.result_template('Hello <%= name %>, Welcome to <%= place %>!', name: 'Neville', place: 'Home')
12
+
13
+ assert_equal 'Hello Neville, Welcome to Home!', result
14
+ end
15
+
16
+ def test_result_missing
17
+
18
+ assert_raises NameError do
19
+ result = template.result_template('Hello <%= name %>, Welcome to <%= place %>!', place: 'Home')
20
+ end
21
+
22
+ end
23
+
24
+ def test_simple_template
25
+ result = template.load_template('simple.txt.erb', name: 'Neville', place: 'Home')
26
+
27
+ assert_equal 'Hello Neville, Welcome to Home!', result
28
+ end
29
+
30
+ def test_nested_dir
31
+ result = template.load_template('nested/inner.sh.erb')
32
+
33
+ assert_equal "#!/bin/bash", result
34
+ end
35
+
36
+ end