apprentice 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +58 -0
- data/Rakefile +1 -0
- data/apprentice.gemspec +23 -0
- data/bin/apprentice +6 -0
- data/init.d/apprentice.sh +83 -0
- data/lib/apprentice.rb +28 -0
- data/lib/apprentice/checker.rb +21 -0
- data/lib/apprentice/checks/galera.rb +51 -0
- data/lib/apprentice/configuration.rb +60 -0
- data/lib/apprentice/server.rb +40 -0
- data/lib/apprentice/version.rb +3 -0
- data/spec/lib/apprentice_spec.rb +8 -0
- data/spec/spec_helper.rb +17 -0
- metadata +93 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5668ebd248945d42c501dba8c9a41ec836e33fe5
|
4
|
+
data.tar.gz: 1fc8746957d18e8e60197b3be49ada6e141ac6fc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bc00be6fd55cda823fe1be0482449da93abebb77fae7e76f2e12ddc36189a755a1d19c2cd057b74de857a33558dc87d7a2bbc0ea5780b94a3e7f8c09482f6035
|
7
|
+
data.tar.gz: 1ba4da56b061846ad0dbefab3ad37478f17e9c77f5719ca311fbd54a7630071cde6f1f11cafebe6e1d2858f81652c097201e42809579decb154dd9db28965fa0
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Moritz Heiber
|
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,58 @@
|
|
1
|
+
# Apprentice
|
2
|
+
|
3
|
+
Apprentice is tiny server application that determines the current state of a running [MariaDB Galera master-master cluster setup](https://mariadb.com/kb/en/what-is-mariadb-galera-cluster/) and responds to HTTP requests on a pre-defined port, depending on the state of the server it is checking on.
|
4
|
+
|
5
|
+
## How does it work?
|
6
|
+
|
7
|
+
You can find out about the syntax by running `apprentice --help`:
|
8
|
+
|
9
|
+
$ apprentice --help
|
10
|
+
Usage: apprentice [options]
|
11
|
+
|
12
|
+
Specific options:
|
13
|
+
-s, --server SERVER Connect to SERVER
|
14
|
+
-u, --user USER USER to connect the server with
|
15
|
+
-p, --password PASSWORD PASSWORD to use
|
16
|
+
-i, --ip IP Local IP to bind to
|
17
|
+
--port PORT Local PORT to use
|
18
|
+
--sql_port PORT Port of the MariaDB server to connect to
|
19
|
+
--[no-]accept-donor Accept cluster state "Donor/Desynced" as valid
|
20
|
+
|
21
|
+
Common options:
|
22
|
+
-h, --help Show this message
|
23
|
+
-v, --version Show version
|
24
|
+
|
25
|
+
|
26
|
+
## What it does
|
27
|
+
|
28
|
+
It determines whether or not the server it is connected to is alive and ready to serve connections to clients. Furthermore, it also determines whether said server is a healthy part of the MariaDB cluster it belongs.
|
29
|
+
|
30
|
+
## What it doesn't do
|
31
|
+
|
32
|
+
* **Loadbalancing**: In turn, it's ment to supply loadbalancers with data on whether or not to include a certain node into their balancing pool
|
33
|
+
* **Relay client connections**: Apprentice itself only serves two responses on a pre-determined port:
|
34
|
+
* *`200 OK`*: The server Apprentice is checking is healthy and ready to accept connections
|
35
|
+
* *`503 Service Unavailable`*: The server is unavailable and not ready for connections
|
36
|
+
|
37
|
+
## What's it checking exactly?
|
38
|
+
|
39
|
+
Apprentice checks the following variables:
|
40
|
+
|
41
|
+
* **wsrep_cluster_size**: A cluster size below 2 is considered an error since there must never be one single server inside a cluster setup.
|
42
|
+
* **wsrep_ready**: Shows whether or not the replication is actually running or not. This must return `ON` for the server to be considered
|
43
|
+
* **wsrep_local_state**: This should read `4`, however, you may also use the --donor-allowed flag on the command-line to turn the value `2` into an acceptable value. Whether or not this is a desired state in your environment is at your discretion.
|
44
|
+
* *Note*: The value `2` indicates the server in question is currently being used as a donor to another member of the cluster and might be exhibiting slow-downs and/or erratic behaviour due to elevated network traffic and disc IO. For further explanation please [consult the MariaDB documentation](https://mariadb.com/kb/en/what-is-mariadb-galera-cluster/).
|
45
|
+
|
46
|
+
## That's great and all, but what gives?
|
47
|
+
By itself, Apprentice doesn't do aynthing all that useful. However, it accommodates [HAProxy's httpchk method](http://cbonte.github.io/haproxy-dconv/configuration-1.4.html#option%20httpchk) quite nicely, making it possible to let HAProxy not only balance connection among a large pool of MariaDB cluster servers but also check on the cluster members health while doing so. With it you needn't care about a server dropping out of the cluster and clients still connecting to it since HAProxy and Apprentice are going to take care of that failing member by taking him out of the connection pool.
|
48
|
+
|
49
|
+
## Goodies
|
50
|
+
|
51
|
+
I've included an (untested) init.d script which you may use in order to start Apprentice at boot time.
|
52
|
+
|
53
|
+
## TODO
|
54
|
+
|
55
|
+
* Write better (r)docs. I'm sorry for the abysmal state they're in right now
|
56
|
+
* Be a lot more forgiving when it comes to SQL connection errors/reconnects/server going awol.
|
57
|
+
* Finish the rspec definitions. Sorry for missing out on those as well.
|
58
|
+
* Write a better init script
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/apprentice.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'apprentice/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'apprentice'
|
8
|
+
spec.version = Apprentice::VERSION
|
9
|
+
spec.authors = 'Moritz Heiber'
|
10
|
+
spec.email = %w{moritz.heiber@gmail.com}
|
11
|
+
spec.description = 'A MariaDB cluster integrity checker'
|
12
|
+
spec.summary = 'Check given servers for consistency and replication status'
|
13
|
+
spec.homepage = 'http://github.com/moritzheiber/apprentice'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = %w{lib}
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
end
|
data/bin/apprentice
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
### BEGIN INIT INFO
|
3
|
+
# Provides: apprentice
|
4
|
+
# Required-Start: mysql
|
5
|
+
# Required-Stop:
|
6
|
+
# Default-Start: 2 3 4 5
|
7
|
+
# Default-Stop: 0 1 6
|
8
|
+
# Short-Description: a MariaDB cluster integrity checker
|
9
|
+
### END INIT INFO
|
10
|
+
|
11
|
+
NAME="`basename ${0/.sh/}`"
|
12
|
+
DAEMON="`which apprentice`"
|
13
|
+
PIDFILE="/var/run/${NAME}.pid"
|
14
|
+
|
15
|
+
[ -r /etc/default/${NAME} ] && source /etc/default/${NAME}
|
16
|
+
|
17
|
+
for file in /lib/init/vars.sh /lib/lsb/init-functions ; do
|
18
|
+
source ${file}
|
19
|
+
done
|
20
|
+
|
21
|
+
#
|
22
|
+
# Function that starts the daemon/service
|
23
|
+
#
|
24
|
+
do_start()
|
25
|
+
{
|
26
|
+
log_begin_msg "Starting ${NAME}..."
|
27
|
+
|
28
|
+
if [ ! "${START}" = "true" ]; then
|
29
|
+
log_failure_msg "this service is disabled. Enable it in /etc/default/$NAME"
|
30
|
+
return 2
|
31
|
+
elif [ ! "${SERVER}" ] || [ ! "${PASSWORD}" ] || [ ! ${USER} ] ; then
|
32
|
+
log_failure_msg "Missing variables inside defaults file."
|
33
|
+
return 2
|
34
|
+
fi
|
35
|
+
|
36
|
+
pidfile_dirname=`dirname ${PIDFILE}`
|
37
|
+
|
38
|
+
[ -d "$pidfile_dirname" ] || mkdir -p "$pidfile_dirname"
|
39
|
+
chown $USER:$GROUP "$pidfile_dirname"
|
40
|
+
chmod 0750 "$pidfile_dirname"
|
41
|
+
|
42
|
+
DAEMON_ARGS="--password ${PASSWORD} --user ${USER} --server ${SERVER} ${EXTRA_ARGS}"
|
43
|
+
|
44
|
+
start-stop-daemon --start --background --make-pidfile --quiet \
|
45
|
+
--pidfile ${PIDFILE} --exec ${DAEMON} --test > /dev/null || return 1
|
46
|
+
start-stop-daemon --start --background --make-pidfile --quiet \
|
47
|
+
--pidfile ${PIDFILE} --exec ${DAEMON} -- ${DAEMON_ARGS} || return 2
|
48
|
+
log_end_msg $?
|
49
|
+
}
|
50
|
+
|
51
|
+
do_stop()
|
52
|
+
{
|
53
|
+
log_begin_msg "Stopping ${NAME}..."
|
54
|
+
|
55
|
+
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --signal 15 --pidfile ${PIDFILE}
|
56
|
+
RETVAL="$?"
|
57
|
+
[ "$RETVAL" = 2 ] && return 2
|
58
|
+
[ "$?" = 2 ] && return 2
|
59
|
+
rm -f ${PIDFILE}
|
60
|
+
log_end_msg $?
|
61
|
+
return "$RETVAL"
|
62
|
+
}
|
63
|
+
|
64
|
+
case "$1" in
|
65
|
+
start)
|
66
|
+
do_start
|
67
|
+
;;
|
68
|
+
stop)
|
69
|
+
do_stop
|
70
|
+
;;
|
71
|
+
reload)
|
72
|
+
do_stop
|
73
|
+
do_start
|
74
|
+
;;
|
75
|
+
restart|force-reload)
|
76
|
+
do_stop
|
77
|
+
do_start
|
78
|
+
;;
|
79
|
+
*)
|
80
|
+
echo "Usage: ${NAME} {start|stop|restart|reload|force-reload}" >&2
|
81
|
+
exit 3
|
82
|
+
;;
|
83
|
+
esac
|
data/lib/apprentice.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'apprentice/configuration'
|
3
|
+
require 'apprentice/version'
|
4
|
+
require 'apprentice/server'
|
5
|
+
|
6
|
+
module Apprentice
|
7
|
+
class Sentinel
|
8
|
+
include Configuration
|
9
|
+
include Server
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@options = get_config
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
EM.run do
|
17
|
+
Signal.trap('INT') { EventMachine.stop }
|
18
|
+
Signal.trap('TERM') { EventMachine.stop }
|
19
|
+
EventMachine.start_server(
|
20
|
+
@options.ip,
|
21
|
+
@options.port,
|
22
|
+
Server::EventServer,
|
23
|
+
@options
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Checker
|
2
|
+
require 'apprentice/checks/galera'
|
3
|
+
include Galera
|
4
|
+
|
5
|
+
STATES = {1 => 'Joining',2 => 'Donor/Desynced',3 => 'Joined',4 => 'Synced'}
|
6
|
+
CODES = {200 => 'OK',503 => 'Service Unavailable'}
|
7
|
+
|
8
|
+
def format_text(texts)
|
9
|
+
value = ''
|
10
|
+
if !texts.empty?
|
11
|
+
texts.each do |t|
|
12
|
+
value << "#{t}\r\n"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
return value
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate_response(code = 503, text)
|
19
|
+
"HTTP/1.1 #{code} #{CODES[code]}\r\nContent-type: text/plain\r\nContent-length: #{text.length}\r\n\r\n#{text}"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Galera
|
2
|
+
def get_galera_status
|
3
|
+
begin
|
4
|
+
result = @client.query "SHOW STATUS LIKE 'wsrep_%';"
|
5
|
+
if result.count > 0
|
6
|
+
result.each do |r|
|
7
|
+
@status.merge!(Hash[*r])
|
8
|
+
end
|
9
|
+
end
|
10
|
+
rescue Exception => message
|
11
|
+
puts message
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def run_checks
|
16
|
+
get_galera_status
|
17
|
+
unless @status.empty?
|
18
|
+
response = {code: 200, text: []}
|
19
|
+
if !check_cluster_size
|
20
|
+
response[:text] << "Cluster size is #{@status['wsrep_cluster_size']}. Split-brain situation is likely."
|
21
|
+
end
|
22
|
+
if !check_ready_state
|
23
|
+
response[:text] << 'Cluster replication is not running.'
|
24
|
+
end
|
25
|
+
if !check_local_state
|
26
|
+
response[:text] << "Local state is '#{STATES[@status['wsrep_local_state']]}'."
|
27
|
+
end
|
28
|
+
response[:code] = 503 unless response[:text].empty?
|
29
|
+
return response
|
30
|
+
else
|
31
|
+
return {code: 503, text: ['Unable to determine cluster status']}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_cluster_size
|
36
|
+
return true if Integer(@status['wsrep_cluster_size']) > 1
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_ready_state
|
41
|
+
return true if @status['wsrep_ready'] == 'ON'
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_local_state
|
46
|
+
s = Integer(@status['wsrep_local_state'])
|
47
|
+
return true if s == 4 || (s == 2 && @donor_allowed)
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Configuration
|
5
|
+
def get_config
|
6
|
+
options = OpenStruct.new
|
7
|
+
options.ip = '0.0.0.0'
|
8
|
+
options.port = 3307
|
9
|
+
options.sql_port = 3306
|
10
|
+
options.accept_donor = false
|
11
|
+
|
12
|
+
opt_parser = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: apprentice [options]\n"
|
14
|
+
opts.separator ''
|
15
|
+
opts.separator 'Specific options:'
|
16
|
+
|
17
|
+
opts.on('-s SERVER', '--server SERVER',
|
18
|
+
'Connect to SERVER') { |s| options.server = s }
|
19
|
+
opts.on('-u USER', '--user USER',
|
20
|
+
'USER to connect the server with') { |u| options.user = u }
|
21
|
+
opts.on('-p PASSWORD', '--password PASSWORD',
|
22
|
+
'PASSWORD to use') { |p| options.password = p }
|
23
|
+
|
24
|
+
opts.on('-i', '--ip IP',
|
25
|
+
'Local IP to bind to') { |i| options.ip = i }
|
26
|
+
opts.on('--port PORT',
|
27
|
+
'Local PORT to use') { |p| options.port = p }
|
28
|
+
opts.on('--sql_port PORT',
|
29
|
+
'Port of the MariaDB server to connect to') { |p| options.sql_port = p }
|
30
|
+
opts.on('--[no-]accept-donor',
|
31
|
+
'Accept cluster state "Donor/Desynced" as valid') { |ad| options.accept_donor = ad }
|
32
|
+
|
33
|
+
opts.separator ''
|
34
|
+
opts.separator 'Common options:'
|
35
|
+
|
36
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
37
|
+
puts opts
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
41
|
+
puts "Apprentice #{Apprentice::VERSION}"
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
begin
|
47
|
+
ARGV << 's-h' if ARGV.size < 3
|
48
|
+
opt_parser.parse!(ARGV)
|
49
|
+
unless options.server && options.user && options.password
|
50
|
+
$stderr.puts 'Error: you have to specify a user, a password and the server to connect to'
|
51
|
+
$stderr.puts 'Try -h/--help for more options'
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
return options
|
55
|
+
rescue OptionParser::ParseError
|
56
|
+
$stderr.print "Error: #{$!}\n"
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Server
|
2
|
+
class EventServer < EM::Connection
|
3
|
+
require 'apprentice/checker'
|
4
|
+
require 'mysql2/em'
|
5
|
+
include Checker
|
6
|
+
|
7
|
+
attr_accessor :client
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@ip = options.ip
|
11
|
+
@port = options.port
|
12
|
+
@sql_port = options.sql_port
|
13
|
+
@server = options.server
|
14
|
+
@user = options.user
|
15
|
+
@password = options.password
|
16
|
+
@donor_allowed = options.donor_allowed
|
17
|
+
@status = {}
|
18
|
+
|
19
|
+
begin
|
20
|
+
@client = Mysql2::Client.new(
|
21
|
+
host: @server,
|
22
|
+
port: @sql_port,
|
23
|
+
username: @user,
|
24
|
+
password: @password,
|
25
|
+
as: :array,
|
26
|
+
reconnect: true
|
27
|
+
)
|
28
|
+
rescue Exception => message
|
29
|
+
puts message
|
30
|
+
EM.stop_server
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def receive_data(data)
|
35
|
+
response = run_checks
|
36
|
+
response_text = format_text(response[:text])
|
37
|
+
send_data generate_response(response[:code], response_text)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# This file was generated by the `rspec --init.d` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
config.filter_run :focus
|
11
|
+
|
12
|
+
# Run specs in random order to surface order dependencies. If you find an
|
13
|
+
# order dependency and want to debug it, you can fix the order by providing
|
14
|
+
# the seed, which is printed after each run.
|
15
|
+
# --seed 1234
|
16
|
+
config.order = 'random'
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: apprentice
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Moritz Heiber
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A MariaDB cluster integrity checker
|
42
|
+
email:
|
43
|
+
- moritz.heiber@gmail.com
|
44
|
+
executables:
|
45
|
+
- apprentice
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- .gitignore
|
50
|
+
- .rspec
|
51
|
+
- Gemfile
|
52
|
+
- LICENSE.txt
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- apprentice.gemspec
|
56
|
+
- bin/apprentice
|
57
|
+
- init.d/apprentice.sh
|
58
|
+
- lib/apprentice.rb
|
59
|
+
- lib/apprentice/checker.rb
|
60
|
+
- lib/apprentice/checks/galera.rb
|
61
|
+
- lib/apprentice/configuration.rb
|
62
|
+
- lib/apprentice/server.rb
|
63
|
+
- lib/apprentice/version.rb
|
64
|
+
- spec/lib/apprentice_spec.rb
|
65
|
+
- spec/spec_helper.rb
|
66
|
+
homepage: http://github.com/moritzheiber/apprentice
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 2.0.7
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Check given servers for consistency and replication status
|
90
|
+
test_files:
|
91
|
+
- spec/lib/apprentice_spec.rb
|
92
|
+
- spec/spec_helper.rb
|
93
|
+
has_rdoc:
|