ey_stonith 0.3.6 → 0.4.1.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/Gemfile +3 -0
- data/Rakefile +26 -0
- data/ey_stonith.gemspec +36 -0
- data/features/check.feature +57 -0
- data/features/cron.feature +67 -0
- data/features/fixtures/dead.ru +6 -0
- data/features/fixtures/healthy.ru +1 -0
- data/features/help.feature +7 -0
- data/features/not_found.feature +24 -0
- data/features/notify.feature +35 -0
- data/features/resume.feature +49 -0
- data/features/steps/stonith_steps.rb +40 -0
- data/features/stop.feature +48 -0
- data/features/support/env.rb +77 -0
- data/lib/ey_stonith/awsm_notifier.rb +13 -8
- data/lib/ey_stonith/commands/abstract.rb +0 -4
- data/lib/ey_stonith/commands/check.rb +13 -36
- data/lib/ey_stonith/commands/claim.rb +5 -96
- data/lib/ey_stonith/commands/cron.rb +3 -5
- data/lib/ey_stonith/commands/info.rb +1 -4
- data/lib/ey_stonith/commands/not_found.rb +1 -0
- data/lib/ey_stonith/commands/notify.rb +16 -55
- data/lib/ey_stonith/commands/reset.rb +0 -1
- data/lib/ey_stonith/commands/stop.rb +0 -1
- data/lib/ey_stonith/commands/takeover.rb +5 -90
- data/lib/ey_stonith/commands.rb +1 -1
- data/lib/ey_stonith/config.rb +17 -66
- data/lib/ey_stonith/rackapp.rb +26 -2
- data/lib/ey_stonith.rb +8 -15
- data/spec/config_spec.rb +53 -0
- data/spec/fixtures/config.yml +11 -0
- data/spec/fixtures/empty.yml +1 -0
- data/spec/helpers.rb +15 -0
- data/spec/history_spec.rb +58 -0
- data/spec/rackapp_spec.rb +100 -0
- data/spec/spec_helper.rb +24 -0
- metadata +240 -60
- data/lib/ey_stonith/address_stealer.rb +0 -40
- data/lib/ey_stonith/check_recorder.rb +0 -55
- data/lib/ey_stonith/data.rb +0 -11
- data/lib/ey_stonith/database.rb +0 -78
@@ -2,110 +2,19 @@ module EY
|
|
2
2
|
module Stonith
|
3
3
|
module Commands
|
4
4
|
class Claim < Abstract
|
5
|
-
|
6
5
|
def self.command
|
7
6
|
'claim'
|
8
7
|
end
|
9
8
|
|
10
9
|
def self.banner
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def claim_path
|
15
|
-
@claim_path ||= config.claim_path
|
10
|
+
"Legacy command. No longer used."
|
16
11
|
end
|
17
12
|
|
18
13
|
def invoke
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
abort_on_existing_data
|
24
|
-
|
25
|
-
attempts = (claim_path.read || 0).to_i.succ
|
26
|
-
|
27
|
-
if @force || attempts >= 6
|
28
|
-
reclaim!
|
29
|
-
else
|
30
|
-
persist_reclaim_attempt(attempts)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def abort_on_existing_data
|
35
|
-
return unless master_hostname
|
36
|
-
|
37
|
-
if config.meta_data_hostname == master_hostname
|
38
|
-
abort "Already claimed, not claiming."
|
39
|
-
else
|
40
|
-
claim_path.delete
|
41
|
-
abort "#{master_hostname} is master, not claiming.\nRemoving stale claim file."
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def reclaim!
|
46
|
-
Stonith.logger.info @force ? "Reclaim forced." : "Reclaim after 6 consecutive checks."
|
47
|
-
claim_path.delete
|
48
|
-
invoke_without_claim_file
|
49
|
-
end
|
50
|
-
|
51
|
-
def persist_reclaim_attempt(attempts)
|
52
|
-
claim_path.open("w") { |f| f << attempts }
|
53
|
-
|
54
|
-
abort <<-ERROR
|
55
|
-
Master unknown and claim file exists, not claiming.
|
56
|
-
Reclaim at 6th attempt or use --force
|
57
|
-
|
58
|
-
Failed attempts: #{attempts}
|
59
|
-
ERROR
|
60
|
-
end
|
61
|
-
|
62
|
-
def invoke_without_claim_file
|
63
|
-
@force ? database.reset : confirm_master!
|
64
|
-
|
65
|
-
data = Data.new(config.meta_data_hostname, config.meta_data_id, config.meta_data_ip)
|
66
|
-
database.set data
|
67
|
-
claim_path.open('w') {}
|
68
|
-
|
69
|
-
Stonith.logger.info "Claimed with data: #{data}"
|
70
|
-
history << :claim
|
71
|
-
end
|
72
|
-
|
73
|
-
def confirm_master!
|
74
|
-
# TODO: Only claim if the master has an IP?
|
75
|
-
# if fog.servers.get(config.meta_data_id).addresses.empty?
|
76
|
-
# abort "No IP, not claiming." + (@force ? "\nIgnoring --force" : "")
|
77
|
-
# end
|
78
|
-
|
79
|
-
confirm_master_with_database
|
80
|
-
confirm_master_with_config
|
81
|
-
end
|
82
|
-
|
83
|
-
def confirm_master_with_database
|
84
|
-
return unless master_hostname
|
85
|
-
|
86
|
-
if config.meta_data_hostname == master_hostname
|
87
|
-
claim_path.open('w') {}
|
88
|
-
abort "Already claimed, not claiming. Touching claim file."
|
89
|
-
else
|
90
|
-
abort "#{master_hostname} is master, not claiming."
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def confirm_master_with_config
|
95
|
-
if config.meta_data_hostname != config.monitor_host
|
96
|
-
abort "#{config.monitor_host} is master, not claiming."
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def master_hostname
|
101
|
-
@master_hostname ||= database.with_data { |data| data.hostname }
|
102
|
-
end
|
103
|
-
|
104
|
-
def parser
|
105
|
-
super.on('-f', '--force', "Force the command (only applicable to claim currently)") do |f|
|
106
|
-
@force = f
|
107
|
-
end
|
108
|
-
super
|
14
|
+
puts <<-HELP
|
15
|
+
Yo Dawg, I heard you like Claim, but we removed it because we don't do that anymore, so stop using it.
|
16
|
+
There is no replacement command needed. Just update instances to update the master.
|
17
|
+
HELP
|
109
18
|
end
|
110
19
|
end
|
111
20
|
end
|
@@ -13,7 +13,7 @@ module EY
|
|
13
13
|
def invoke
|
14
14
|
Stonith.logger.info "Stonith started"
|
15
15
|
heartbeat_loop do |beat|
|
16
|
-
unless_stopped {
|
16
|
+
unless_stopped { run_command if beat.zero? }
|
17
17
|
sleep 1
|
18
18
|
end
|
19
19
|
end
|
@@ -30,10 +30,8 @@ module EY
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
def
|
34
|
-
|
35
|
-
system("#{script} #{cmd}#{command_options}")
|
36
|
-
end
|
33
|
+
def run_command
|
34
|
+
system("#{script} check#{command_options}")
|
37
35
|
end
|
38
36
|
end
|
39
37
|
end
|
@@ -7,14 +7,11 @@ module EY
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.banner
|
10
|
-
"Print the full config file
|
10
|
+
"Print the full config file"
|
11
11
|
end
|
12
12
|
|
13
13
|
def invoke
|
14
14
|
puts config
|
15
|
-
puts "Database:"
|
16
|
-
data = database.with_data { |data| data }
|
17
|
-
puts " #{data}"
|
18
15
|
end
|
19
16
|
end
|
20
17
|
end
|
@@ -7,7 +7,7 @@ module EY
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.banner
|
10
|
-
'Notify the provisioning server
|
10
|
+
'Notify the provisioning server that master is down.'
|
11
11
|
end
|
12
12
|
|
13
13
|
def notify_path
|
@@ -15,70 +15,31 @@ module EY
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def invoke
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
database.with_data do |data|
|
23
|
-
abort_if_master data.hostname
|
24
|
-
notify!
|
25
|
-
end
|
26
|
-
|
27
|
-
abort_no_data
|
28
|
-
end
|
29
|
-
|
30
|
-
def abort_if_unintentional
|
31
|
-
return if @force || notify_path.exist?
|
32
|
-
|
33
|
-
abort <<-ERROR
|
34
|
-
Cannot notify, #{notify_path} does not exist.
|
35
|
-
If you want to (destructively) notify AWSM that this instance is master, call with --force.
|
36
|
-
ERROR
|
37
|
-
end
|
38
|
-
|
39
|
-
def abort_if_master(hostname)
|
40
|
-
if config.meta_data_hostname != hostname
|
41
|
-
Stonith.logger.error "Cannot notify, I am not master. Master is #{hostname}. Cancelling notify."
|
42
|
-
abort "Cannot notify, I am not master."
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def abort_no_data
|
47
|
-
msg = "Cannot notify, there is no master in the database. Giving up."
|
48
|
-
Stonith.logger.error msg
|
49
|
-
abort msg
|
18
|
+
Stonith.logger.warn "Notifying provisioning server that master is down."
|
19
|
+
history << :notify
|
20
|
+
notify!
|
21
|
+
exit
|
50
22
|
end
|
51
23
|
|
52
24
|
def notify!
|
53
|
-
notifier = AwsmNotifier.new
|
54
|
-
|
25
|
+
notifier = AwsmNotifier.new(config.notify_uri, config.endpoint_id, config.endpoint_token, {
|
26
|
+
'monitor_host' => config.monitor_host
|
27
|
+
})
|
28
|
+
|
29
|
+
notifier.notify method(:success), method(:unreachable)
|
55
30
|
end
|
56
31
|
|
57
|
-
def success
|
58
|
-
Stonith.logger.info "AWSM notified!"
|
32
|
+
def success(data)
|
59
33
|
history << :notified
|
60
|
-
|
34
|
+
count = data['notification_count'] || 'N/A'
|
35
|
+
takeover = data['takeover'].inspect
|
36
|
+
Stonith.logger.info "Provisioning server notified: (notification_count: #{count}, takeover: #{takeover})"
|
61
37
|
end
|
62
38
|
|
63
39
|
def unreachable
|
64
|
-
|
65
|
-
msg = "Unable to reach AWSM for promotion to master."
|
40
|
+
msg = "Unable to notify provisioning server that master is down."
|
66
41
|
Stonith.logger.warn msg
|
67
|
-
abort "#{msg}\nIf you're running this from the command line, you should run
|
68
|
-
end
|
69
|
-
|
70
|
-
def refused(response_body)
|
71
|
-
msg = "Notify refused by endpoint. Giving up.\nResponse: #{response_body}"
|
72
|
-
Stonith.logger.error msg
|
73
|
-
history << :refused
|
74
|
-
abort msg
|
75
|
-
end
|
76
|
-
|
77
|
-
def parser
|
78
|
-
super.on '-f', '--force', "Force the command (only applicable to claim currently)" do |f|
|
79
|
-
@force = f
|
80
|
-
end
|
81
|
-
super
|
42
|
+
abort "#{msg}\nIf you're running this from the command line, you should run `stonith notify` again."
|
82
43
|
end
|
83
44
|
end
|
84
45
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
|
3
1
|
module EY
|
4
2
|
module Stonith
|
5
3
|
module Commands
|
@@ -9,97 +7,14 @@ module EY
|
|
9
7
|
end
|
10
8
|
|
11
9
|
def self.banner
|
12
|
-
|
10
|
+
"Legacy command. No longer used. Run this command for more info."
|
13
11
|
end
|
14
12
|
|
15
13
|
def invoke
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
set_takeover_lock
|
21
|
-
|
22
|
-
database.with_locked_data do |data|
|
23
|
-
restore_data_on_fail(data)
|
24
|
-
|
25
|
-
if instance_id == data.instance_id
|
26
|
-
locked!(data.instance_id, data.ip)
|
27
|
-
else
|
28
|
-
relent!
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def abort_if_no_instance_id
|
34
|
-
if !instance_id || instance_id == ""
|
35
|
-
abort "Please call with the instance_id of the master to takeover.\n\n#{parser}"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def abort_if_self
|
40
|
-
if instance_id == config.meta_data_id
|
41
|
-
abort "Cannot takeover self!"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def abort_if_takeover_lock
|
46
|
-
if config.takeover_path.exist?
|
47
|
-
abort "Already attempting takeover!\n#{config.takeover_path.read}"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def takeover_path
|
52
|
-
@takeover_path ||= config.takeover_path
|
53
|
-
end
|
54
|
-
|
55
|
-
def set_takeover_lock
|
56
|
-
at_exit { takeover_path.delete if takeover_path.exist? }
|
57
|
-
takeover_path.open('w') { |f| f << "Takeover started at #{Time.now}" }
|
58
|
-
end
|
59
|
-
|
60
|
-
def restore_data_on_fail(data)
|
61
|
-
at_exit do
|
62
|
-
if database.set(data) # always replace the data if it wasn't set
|
63
|
-
Stonith.logger.error("Emergency replacement of redis data #{data.inspect} occurred.")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def locked!(instance_id, master_ip)
|
69
|
-
Stonith.logger.info "Locked! Taking over #{instance_id}."
|
70
|
-
history << :takeover
|
71
|
-
|
72
|
-
ip = steal_address(instance_id, master_ip)
|
73
|
-
data = Data.new(config.meta_data_hostname, config.meta_data_id, ip)
|
74
|
-
database.set data
|
75
|
-
|
76
|
-
takeover_path.delete
|
77
|
-
execute :notify, "--force"
|
78
|
-
end
|
79
|
-
|
80
|
-
def relent!
|
81
|
-
history << :relent
|
82
|
-
msg = "Failed to grab lock, relenting."
|
83
|
-
Stonith.logger.info msg
|
84
|
-
abort msg
|
85
|
-
end
|
86
|
-
|
87
|
-
def steal_address(instance_id, ip)
|
88
|
-
Stonith.logger.info "Stealing IP #{ip} from #{instance_id}."
|
89
|
-
address = AddressStealer.new(instance_id, ip, config.fog_credentials)
|
90
|
-
address.associate(config.meta_data_id)
|
91
|
-
address.ip
|
92
|
-
end
|
93
|
-
|
94
|
-
def instance_id
|
95
|
-
@instance_id
|
96
|
-
end
|
97
|
-
|
98
|
-
def parser
|
99
|
-
super.on('-i', '--instance INSTANCE_ID', "Amazon Instance ID of the failing master") do |instance|
|
100
|
-
@instance_id = instance
|
101
|
-
end
|
102
|
-
super
|
14
|
+
puts <<-HELP
|
15
|
+
Takeovers are no longer performed from the instance side directly.
|
16
|
+
You can cause a takeover by running `stonith notify` 6 times quickly if you really need to.
|
17
|
+
HELP
|
103
18
|
end
|
104
19
|
end
|
105
20
|
end
|
data/lib/ey_stonith/commands.rb
CHANGED
data/lib/ey_stonith/config.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'json'
|
3
|
-
require 'open-uri'
|
4
3
|
|
5
4
|
module EY
|
6
5
|
module Stonith
|
@@ -24,24 +23,15 @@ module EY
|
|
24
23
|
end
|
25
24
|
|
26
25
|
DEFAULT = {
|
27
|
-
'log'
|
28
|
-
'state_dir'
|
29
|
-
'heartbeat'
|
30
|
-
'endpoint_uri'
|
31
|
-
'endpoint_token'
|
32
|
-
|
33
|
-
'monitor_host'
|
34
|
-
'monitor_path'
|
26
|
+
'log' => '/var/log/stonith.log',
|
27
|
+
'state_dir' => '/var/run/stonith',
|
28
|
+
'heartbeat' => 10,
|
29
|
+
'endpoint_uri' => nil,
|
30
|
+
'endpoint_token' => nil,
|
31
|
+
'endpoint_id' => nil,
|
32
|
+
'monitor_host' => nil,
|
33
|
+
'monitor_path' => '/haproxy/monitor',
|
35
34
|
'monitor_timeout' => 5,
|
36
|
-
|
37
|
-
'redis_host' => nil,
|
38
|
-
'redis_port' => 6379,
|
39
|
-
'redis_key' => 'stonith',
|
40
|
-
'redis_db' => 14,
|
41
|
-
'redis_timeout' => 60 * 5,
|
42
|
-
|
43
|
-
'aws_secret_id' => nil,
|
44
|
-
'aws_secret_key' => nil,
|
45
35
|
}
|
46
36
|
|
47
37
|
def initialize(config_path)
|
@@ -65,71 +55,36 @@ Config:
|
|
65
55
|
|
66
56
|
state_path: #{state_path}
|
67
57
|
stop_path: #{stop_path}
|
68
|
-
claim_path: #{claim_path}
|
69
|
-
checks_path: #{checks_path}
|
70
|
-
notify_path: #{notify_path}
|
71
58
|
history_path: #{history_path}
|
72
|
-
|
59
|
+
success_path: #{success_path}
|
73
60
|
|
74
|
-
endpoint_uri:
|
61
|
+
endpoint_uri: #{endpoint_uri}
|
75
62
|
endpoint_token: #{endpoint_token}
|
76
|
-
|
77
|
-
|
78
|
-
awsm_credentials: #{awsm_credentials.inspect}
|
79
|
-
|
80
|
-
meta_data_hostname: #{meta_data_hostname}
|
81
|
-
meta_data_id: #{meta_data_id}
|
82
|
-
meta_data_ip: #{meta_data_ip}
|
63
|
+
endpoint_id: #{endpoint_id}
|
64
|
+
notify_uri: #{notify_uri}
|
83
65
|
|
84
|
-
monitor_host:
|
85
|
-
monitor_path:
|
66
|
+
monitor_host: #{monitor_host}
|
67
|
+
monitor_path: #{monitor_path}
|
86
68
|
monitor_timeout: #{monitor_timeout}
|
87
|
-
|
88
|
-
redis_host: #{redis_host}
|
89
|
-
redis_port: #{redis_port}
|
90
|
-
redis_key: #{redis_key}
|
91
|
-
redis_db: #{redis_db}
|
92
|
-
redis_timeout: #{redis_timeout}
|
93
69
|
STRING
|
94
70
|
end
|
95
71
|
|
96
72
|
def log_path() pathname(log) end
|
97
73
|
def state_path() ensure_exists(pathname(state_dir)) end
|
98
74
|
def stop_path() state_path + 'stop' end
|
99
|
-
def claim_path() state_path + 'claim' end
|
100
|
-
def checks_path() state_path + 'checks' end
|
101
|
-
def notify_path() state_path + 'notify' end
|
102
75
|
def history_path() state_path + 'history' end
|
103
|
-
def
|
76
|
+
def success_path() state_path + 'success' end
|
104
77
|
def endpoint_uri() URI.parse(method_missing('endpoint_uri')) end
|
105
|
-
def
|
78
|
+
def notify_uri
|
106
79
|
result = endpoint_uri
|
107
|
-
result.path <<
|
80
|
+
result.path << NOTIFY_PATH
|
108
81
|
result
|
109
82
|
end
|
110
83
|
|
111
|
-
def meta_data_hostname() @data['meta_data_hostname'] ||= meta_data('local-hostname') end
|
112
|
-
def meta_data_id() @data['meta_data_id'] ||= meta_data('instance-id') end
|
113
|
-
def meta_data_ip() @data['meta_data_ip'] || meta_data('public-ipv4') end # don't cache
|
114
|
-
|
115
84
|
def respond_to?(meth)
|
116
85
|
@data.key?(meth) || super
|
117
86
|
end
|
118
87
|
|
119
|
-
def awsm_credentials
|
120
|
-
{
|
121
|
-
'aws_secret_id' => aws_secret_id,
|
122
|
-
'aws_secret_key' => aws_secret_key,
|
123
|
-
}
|
124
|
-
end
|
125
|
-
|
126
|
-
def fog_credentials
|
127
|
-
{
|
128
|
-
:aws_access_key_id => aws_secret_id,
|
129
|
-
:aws_secret_access_key => aws_secret_key,
|
130
|
-
}
|
131
|
-
end
|
132
|
-
|
133
88
|
private
|
134
89
|
|
135
90
|
def self.secret_hack
|
@@ -139,10 +94,6 @@ Config:
|
|
139
94
|
def self.pwned?
|
140
95
|
@secret_hack
|
141
96
|
end
|
142
|
-
|
143
|
-
def meta_data(key)
|
144
|
-
open("http://169.254.169.254/latest/meta-data/#{key}").read
|
145
|
-
end
|
146
97
|
|
147
98
|
def pathname(path) path && Pathname.new(path) end
|
148
99
|
|
data/lib/ey_stonith/rackapp.rb
CHANGED
@@ -4,8 +4,12 @@ require 'json'
|
|
4
4
|
module EY
|
5
5
|
module Stonith
|
6
6
|
class Rackapp < Sinatra::Base
|
7
|
+
enable :raise_errors
|
8
|
+
disable :dump_errors
|
9
|
+
disable :show_exceptions
|
10
|
+
|
7
11
|
before { content_type :json }
|
8
|
-
get('/') {
|
12
|
+
get('/') { '' }
|
9
13
|
|
10
14
|
get HEALTH_PATH do
|
11
15
|
unless EY::Stonith.healthy?
|
@@ -15,14 +19,34 @@ module EY
|
|
15
19
|
end
|
16
20
|
|
17
21
|
post TAKEOVER_PATH do
|
18
|
-
|
22
|
+
if valid?
|
23
|
+
unless EY::Stonith.takeover(label)
|
24
|
+
status 410
|
25
|
+
end
|
26
|
+
else
|
19
27
|
status 410
|
20
28
|
end
|
21
29
|
{}.to_json
|
22
30
|
end
|
23
31
|
|
32
|
+
post NOTIFY_PATH do
|
33
|
+
if valid?
|
34
|
+
EY::Stonith.notify(label, params[:monitor_host])
|
35
|
+
{
|
36
|
+
'notification_count' => '1',
|
37
|
+
'takeover' => false
|
38
|
+
}.to_json
|
39
|
+
else
|
40
|
+
status 403
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
24
44
|
private
|
25
45
|
|
46
|
+
def valid?
|
47
|
+
EY::Stonith.valid?(label, token)
|
48
|
+
end
|
49
|
+
|
26
50
|
def label() params['label'] end
|
27
51
|
def token() params['token'] end
|
28
52
|
end
|
data/lib/ey_stonith.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'logger'
|
2
|
+
require 'open-uri'
|
2
3
|
|
3
4
|
$LOAD_PATH.unshift(File.expand_path('..', __FILE__))
|
4
5
|
|
@@ -6,25 +7,15 @@ module EY
|
|
6
7
|
module Stonith
|
7
8
|
class Error < StandardError; end
|
8
9
|
|
9
|
-
autoload :AbstractMaster, 'ey_stonith/abstract_master'
|
10
10
|
autoload :AwsmNotifier, 'ey_stonith/awsm_notifier'
|
11
|
-
autoload :AddressStealer, 'ey_stonith/address_stealer'
|
12
|
-
autoload :Box, 'ey_stonith/box'
|
13
|
-
autoload :CheckRecorder, 'ey_stonith/check_recorder'
|
14
11
|
autoload :Commands, 'ey_stonith/commands'
|
15
|
-
autoload :CLI, 'ey_stonith/cli'
|
16
12
|
autoload :Config, 'ey_stonith/config'
|
17
|
-
autoload :Data, 'ey_stonith/data'
|
18
|
-
autoload :Database, 'ey_stonith/database'
|
19
13
|
autoload :History, 'ey_stonith/history'
|
20
|
-
autoload :LocalMaster, 'ey_stonith/local_master'
|
21
|
-
autoload :Master, 'ey_stonith/master'
|
22
|
-
autoload :MetaData, 'ey_stonith/meta_data'
|
23
14
|
autoload :Rackapp, 'ey_stonith/rackapp'
|
24
|
-
autoload :Slave, 'ey_stonith/slave'
|
25
15
|
|
26
16
|
HEALTH_PATH = '/health' unless defined? HEALTH_PATH
|
27
17
|
TAKEOVER_PATH = '/takeover' unless defined? TAKEOVER_PATH
|
18
|
+
NOTIFY_PATH = '/notify' unless defined? NOTIFY_PATH
|
28
19
|
|
29
20
|
def self.log_to(io)
|
30
21
|
@@logger = Logger.new io
|
@@ -38,10 +29,12 @@ module EY
|
|
38
29
|
|
39
30
|
def self.healthy?() callback_module.healthy? end
|
40
31
|
|
41
|
-
def self.takeover(label
|
42
|
-
|
43
|
-
|
44
|
-
|
32
|
+
def self.takeover(label)
|
33
|
+
callback_module.takeover label
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.notify(label, monitor_host)
|
37
|
+
callback_module.notify label, monitor_host
|
45
38
|
end
|
46
39
|
|
47
40
|
def self.valid?(label, token)
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
describe EY::Stonith::Config do
|
5
|
+
before { described_class.send(:secret_hack) }
|
6
|
+
def fixture_path(file)
|
7
|
+
Pathname.new(File.expand_path(File.join('../fixtures', file), __FILE__))
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "default" do
|
11
|
+
subject { described_class.new(fixture_path('empty.yml')) }
|
12
|
+
|
13
|
+
def self.it_requires(key)
|
14
|
+
it(key) { lambda { subject.send(key) }.should raise_error(EY::Stonith::Config::RequiredSetting) }
|
15
|
+
end
|
16
|
+
|
17
|
+
its(:path) { should == fixture_path('empty.yml') }
|
18
|
+
its(:log_path) { should == Pathname.new('/var/log/stonith.log') }
|
19
|
+
its(:state_path) { should == Pathname.new('/var/run/stonith') }
|
20
|
+
its(:stop_path) { should == Pathname.new('/var/run/stonith/stop') }
|
21
|
+
its(:history_path) { should == Pathname.new('/var/run/stonith/history') }
|
22
|
+
its(:success_path) { should == Pathname.new('/var/run/stonith/success') }
|
23
|
+
its(:heartbeat) { should == 10 }
|
24
|
+
|
25
|
+
it_requires(:monitor_host)
|
26
|
+
its(:monitor_path) { should == '/haproxy/monitor' }
|
27
|
+
its(:monitor_timeout) { should == 5 }
|
28
|
+
|
29
|
+
it_requires(:endpoint_uri)
|
30
|
+
it_requires(:endpoint_token)
|
31
|
+
it_requires(:endpoint_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "overridden" do
|
35
|
+
subject { described_class.new(fixture_path('config.yml')) }
|
36
|
+
|
37
|
+
its(:path) { should == fixture_path('config.yml') }
|
38
|
+
its(:log_path) { should == Pathname.new('var/log/stonith.log') }
|
39
|
+
its(:state_path) { should == Pathname.new('var/run/stonith') }
|
40
|
+
its(:stop_path) { should == Pathname.new('var/run/stonith/stop') }
|
41
|
+
its(:history_path) { should == Pathname.new('var/run/stonith/history') }
|
42
|
+
its(:success_path) { should == Pathname.new('var/run/stonith/success') }
|
43
|
+
its(:heartbeat) { should == 1 }
|
44
|
+
|
45
|
+
its(:monitor_host) { should == 'localhost' }
|
46
|
+
its(:monitor_path) { should == '/chickity/check' }
|
47
|
+
its(:monitor_timeout) { should == 10 }
|
48
|
+
|
49
|
+
its(:endpoint_uri) { should == URI.parse('http://example.com/stonith') }
|
50
|
+
its(:endpoint_token) { should == 'i-87654321' }
|
51
|
+
its(:endpoint_id) { should == "i-87654321" }
|
52
|
+
end
|
53
|
+
end
|