ey_stonith 0.3.6 → 0.4.1.pre
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/.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
|