elastic_manager 0.1.2 → 0.1.3
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.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/elastic_manager.gemspec +6 -4
- data/lib/elastic_manager.rb +31 -152
- data/lib/elastic_manager/config.rb +48 -32
- data/lib/elastic_manager/request.rb +188 -0
- data/lib/elastic_manager/utils.rb +17 -0
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59956a9221433200e4da7152617c039ac1dde775d30299876b6bb379a2358fde
|
4
|
+
data.tar.gz: c26c03c09b73188ef4e833a580981f538949918538e7df072f74a6eb5d55fd47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4b322f736dcc9fea634113b7f661e49827e8baf26ccdcbd0e8f0c40c2e44c21fc5bf146abf04a751f8321fa157156f5a5fa4f6bfd6165edc89f96a3604103af
|
7
|
+
data.tar.gz: 6a06140123e5dc85b40b21ce47d115982c7293d6518ace6332614fcbba7e6a02b6d5b31636505d16a10a88c4bdb1210a140aa0612224e1a130fe164a81d4bb5c
|
data/README.md
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
[](https://travis-ci.org/onetwotrip/elastic_manager)
|
data/elastic_manager.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'elastic_manager'
|
3
3
|
s.executables = ['elastic_manager']
|
4
|
-
s.version = '0.1.
|
4
|
+
s.version = '0.1.3'
|
5
5
|
s.date = '2018-10-15'
|
6
6
|
s.summary = 'Because qurator sucks'
|
7
7
|
s.description = 'Manager for logstash indices in elastic'
|
@@ -12,7 +12,9 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.homepage = 'https://github.com/onetwotrip/elastic_manager'
|
13
13
|
s.license = 'MIT'
|
14
14
|
s.required_ruby_version = '>= 2.0.0'
|
15
|
-
s.add_dependency 'http',
|
16
|
-
s.add_dependency 'colorize',
|
17
|
-
s.add_dependency 'dotenv',
|
15
|
+
s.add_dependency 'http', '~> 3.3'
|
16
|
+
s.add_dependency 'colorize', '~> 0.8'
|
17
|
+
s.add_dependency 'dotenv', '~> 2.4'
|
18
|
+
s.add_dependency 'rake', '~> 12.3'
|
19
|
+
s.add_dependency 'yajl-ruby', '~> 1.4'
|
18
20
|
end
|
data/lib/elastic_manager.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
require 'dotenv/load'
|
2
2
|
require 'date'
|
3
|
-
require 'http'
|
4
3
|
require 'elastic_manager/config'
|
5
4
|
require 'elastic_manager/logger'
|
5
|
+
require 'elastic_manager/request'
|
6
|
+
require 'elastic_manager/utils'
|
6
7
|
|
7
8
|
class ElasticManager
|
8
|
-
|
9
9
|
include Config
|
10
10
|
include Logging
|
11
|
+
include Request
|
12
|
+
include Utils
|
11
13
|
|
12
|
-
attr_reader :config
|
14
|
+
# attr_reader :config
|
13
15
|
|
14
16
|
def initialize(argv)
|
15
17
|
if argv.size == 0
|
@@ -17,87 +19,8 @@ class ElasticManager
|
|
17
19
|
else
|
18
20
|
@config = load_from_argv(argv)
|
19
21
|
end
|
20
|
-
end
|
21
22
|
|
22
|
-
|
23
|
-
obj.to_s.downcase == 'true'
|
24
|
-
end
|
25
|
-
|
26
|
-
def es_request(verb, uri, json = {})
|
27
|
-
tries ||= @config['retry'].to_i
|
28
|
-
|
29
|
-
request_uri = "#{@config['es']['url']}#{uri}"
|
30
|
-
|
31
|
-
response = HTTP.timeout(
|
32
|
-
write: @config['timeout']['write'].to_i,
|
33
|
-
connect: @config['timeout']['connect'].to_i,
|
34
|
-
read: @config['timeout']['read'].to_i
|
35
|
-
).headers(
|
36
|
-
'Accept': 'application/json',
|
37
|
-
'Content-type': 'application/json'
|
38
|
-
).request(
|
39
|
-
verb.to_sym,
|
40
|
-
request_uri,
|
41
|
-
json: json
|
42
|
-
)
|
43
|
-
|
44
|
-
if response.code == 200 || response.code == 404
|
45
|
-
# TODO (anton.raybov): mb we need to return only one value but empty if 404???
|
46
|
-
return response.code, response.body.to_s
|
47
|
-
else
|
48
|
-
log.fatal "error in request - url: #{request_uri}, status: #{response.code}, response: #{response.body}"
|
49
|
-
exit 1
|
50
|
-
end
|
51
|
-
|
52
|
-
rescue StandardError => e
|
53
|
-
|
54
|
-
log.warn "try #{tries + 1} '''#{e.message}''' sleeping #{@config['sleep']} sec..."
|
55
|
-
sleep @config['sleep'].to_i
|
56
|
-
|
57
|
-
retry unless (tries -= 1).zero?
|
58
|
-
abort "backtrace:\n\t#{e.backtrace.join("\n\t")}".red
|
59
|
-
end
|
60
|
-
|
61
|
-
def es_green?
|
62
|
-
status, response = es_request('get', '/_cluster/health')
|
63
|
-
return JSON.parse(response)['status'] == 'green' if status == 200
|
64
|
-
false
|
65
|
-
end
|
66
|
-
|
67
|
-
def es_all_indices(from=nil, to=nil, state=nil, type=nil)
|
68
|
-
req_path = '/_cluster/state/metadata/'
|
69
|
-
req_params = '?filter_path=metadata.indices.*.state,'
|
70
|
-
req_params << 'metadata.indices.*.settings.index.routing.allocation.require.box_type'
|
71
|
-
|
72
|
-
status, response = es_request('get', req_path + req_params)
|
73
|
-
if status == 200
|
74
|
-
indices = JSON.parse(response)['metadata']['indices']
|
75
|
-
else
|
76
|
-
log.fatal "can't work with all_indices response was: #{status} - #{response}"
|
77
|
-
exit 1
|
78
|
-
end
|
79
|
-
|
80
|
-
indices.select!{ |k, v| v['state'] == state } if state
|
81
|
-
|
82
|
-
if type
|
83
|
-
# TODO (anton.ryabov): next line just for debug purpose, need better handling
|
84
|
-
indices.each { |k, v| log.warn "#{k} - #{v.to_json}" unless v['settings'] }
|
85
|
-
indices.select!{ |k, v| v['settings']['index']['routing']['allocation']['require']['box_type'] == type }
|
86
|
-
end
|
87
|
-
|
88
|
-
res = []
|
89
|
-
indices.each_key do |index|
|
90
|
-
begin
|
91
|
-
index_date = Date.parse(index.gsub('-', ''))
|
92
|
-
rescue ArgumentError => e
|
93
|
-
log.error "#{e.message} for #{index}"
|
94
|
-
next
|
95
|
-
end
|
96
|
-
|
97
|
-
res << URI.escape(index) if (from..to).cover? index_date
|
98
|
-
end
|
99
|
-
|
100
|
-
res
|
23
|
+
@elastic = Request::Elastic.new(@config)
|
101
24
|
end
|
102
25
|
|
103
26
|
def work
|
@@ -107,104 +30,60 @@ class ElasticManager
|
|
107
30
|
date_to = Date.parse(@config['to'])
|
108
31
|
|
109
32
|
unless true?(@config['force'])
|
110
|
-
unless
|
33
|
+
unless @elastic.green?
|
111
34
|
log.fatal "elasticsearch on #{@config['es']['url']} is not green"
|
112
35
|
exit 1
|
113
36
|
end
|
114
37
|
end
|
115
38
|
|
116
39
|
if indices.length == 1 && indices.first == '_all'
|
117
|
-
indices = es_all_indices(date_from, date_to, 'close')
|
40
|
+
# indices = es_all_indices(date_from, date_to, 'close')
|
41
|
+
indices = @elastic.all_indices(date_from, date_to, 'close')
|
118
42
|
end
|
119
43
|
|
120
44
|
date_from.upto(date_to) do |date|
|
121
45
|
date = date.to_s.tr!('-', '.')
|
122
46
|
|
123
47
|
indices.each do |index_name|
|
124
|
-
if @config['
|
125
|
-
|
126
|
-
|
48
|
+
if @config['settings'][index_name]
|
49
|
+
if @config['settings'][index_name]['skip_open']
|
50
|
+
log.debug @config['settings'][index_name]['skip_open'].inspect
|
51
|
+
|
52
|
+
if true?(@config['settings'][index_name]['skip_open'])
|
53
|
+
log.warn "#{index_name} index open skiped"
|
54
|
+
next
|
55
|
+
end
|
56
|
+
end
|
127
57
|
end
|
128
58
|
|
129
59
|
index = "#{index_name}-#{date}"
|
130
60
|
|
131
|
-
|
132
|
-
if status == 404
|
133
|
-
log.warn "#{index} index not found"
|
134
|
-
log.info "trying snapshot restore for #{index}"
|
135
|
-
|
136
|
-
snapshot_name = "snapshot_#{index}"
|
137
|
-
|
138
|
-
# TODO: we need improve this if several snapshot repos used in elastic
|
139
|
-
status, response = es_request('get', '/_snapshot/')
|
140
|
-
if status == 200
|
141
|
-
snapshot_repo = JSON.parse(response).keys.first
|
142
|
-
|
143
|
-
status, response = es_request('get', "/_snapshot/#{snapshot_repo}/#{snapshot_name}/")
|
144
|
-
if status == 200
|
145
|
-
snapshot = JSON.parse(response)['snapshots']
|
146
|
-
if snapshot.size == 1
|
147
|
-
body = {
|
148
|
-
index_settings: {
|
149
|
-
'index.number_of_replicas' => 0,
|
150
|
-
'index.refresh_interval' => -1,
|
151
|
-
'index.routing.allocation.require.box_type' => 'warm'
|
152
|
-
}
|
153
|
-
}
|
154
|
-
status, response = es_request('post', "/_snapshot/#{snapshot_repo}/#{snapshot.first['snapshot']}/_restore", body)
|
61
|
+
response = @elastic.request(:get, "/_cat/indices/#{index}")
|
155
62
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
until restore_ok
|
160
|
-
sleep 30
|
161
|
-
status, response = es_request('get', "/#{index}/_recovery")
|
63
|
+
if response.code == 404
|
64
|
+
log.warn "#{index} index not found"
|
65
|
+
log.info "#{index} trying snapshot restore"
|
162
66
|
|
163
|
-
|
164
|
-
|
165
|
-
end
|
166
|
-
log.info "#{index} restored"
|
167
|
-
else
|
168
|
-
log.fatal "can't restore snapshot response was: #{status} - #{response}"
|
169
|
-
exit 1
|
170
|
-
end
|
171
|
-
else
|
172
|
-
log.fatal "wrong snapshot size"
|
173
|
-
exit 1
|
174
|
-
end
|
175
|
-
else
|
176
|
-
log.fatal "can't work with snapshot response was: #{status} - #{response}"
|
177
|
-
exit 1
|
178
|
-
end
|
67
|
+
if @elastic.restore_snapshot(index)
|
68
|
+
log.info "#{index} restored"
|
179
69
|
else
|
180
|
-
log.
|
181
|
-
exit 1
|
70
|
+
log.error "#{index} troubles with restore"
|
182
71
|
end
|
183
|
-
elsif
|
184
|
-
if response =~ /open/
|
72
|
+
elsif response.code == 200
|
73
|
+
if response.body.to_s =~ /open/
|
185
74
|
log.warn "#{index} index already opened"
|
186
75
|
next
|
187
76
|
end
|
188
77
|
|
189
|
-
|
190
|
-
status, response = es_request('post', "/#{index}/_open?master_timeout=3m")
|
191
|
-
if status == 200
|
192
|
-
response = JSON.parse(response)
|
193
|
-
else
|
194
|
-
log.fatal "wrong response code for #{index} open"
|
195
|
-
exit 1
|
196
|
-
end
|
197
|
-
rescue JSON::ParserError => e
|
198
|
-
log.fatal "json parse err: '''#{e.message}'''\n\t#{e.backtrace.join("\n\t")}"
|
199
|
-
exit 1
|
200
|
-
end
|
201
|
-
|
202
|
-
if response['acknowledged'] == true
|
78
|
+
if @elastic.open_index(index)
|
203
79
|
log.info "#{index} index open success"
|
204
80
|
else
|
205
81
|
log.fatal "#{index} index open failed"
|
206
82
|
exit 1
|
207
83
|
end
|
84
|
+
else
|
85
|
+
log.fatal "can't work with index #{index} response was: #{response.code} - #{response}"
|
86
|
+
exit 1
|
208
87
|
end
|
209
88
|
end
|
210
89
|
end
|
@@ -1,13 +1,14 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yajl'
|
1
3
|
require 'elastic_manager/logger'
|
2
4
|
|
3
5
|
module Config
|
4
|
-
|
5
6
|
include Logging
|
6
7
|
|
7
8
|
MAIN_PARAMS = %w[TASK INDICES FROM TO]
|
8
9
|
MAIN_PARAMS.freeze
|
9
10
|
|
10
|
-
ADDITIONAL_PARAMS = %w[ES_URL TIMEOUT_WRITE TIMEOUT_CONNECT TIMEOUT_READ RETRY SLEEP FORCE
|
11
|
+
ADDITIONAL_PARAMS = %w[ES_URL TIMEOUT_WRITE TIMEOUT_CONNECT TIMEOUT_READ RETRY SLEEP FORCE SETTINGS]
|
11
12
|
ADDITIONAL_PARAMS.freeze
|
12
13
|
|
13
14
|
BANNER_ENV = "Missing argument: #{MAIN_PARAMS.join(', ')}. "
|
@@ -28,11 +29,49 @@ module Config
|
|
28
29
|
default['timeout']['write'] = '2'
|
29
30
|
default['timeout']['connect'] = '3'
|
30
31
|
default['timeout']['read'] = '60'
|
32
|
+
default['settings'] = {}
|
31
33
|
|
32
34
|
log.debug "default config: #{default.inspect}"
|
33
35
|
default
|
34
36
|
end
|
35
37
|
|
38
|
+
def parse_settings(json)
|
39
|
+
begin
|
40
|
+
JSON.parse(json)
|
41
|
+
rescue JSON::ParserError => e
|
42
|
+
log.fatal "json parse err: '''#{e.message}'''\n\t#{e.backtrace.join("\n\t")}"
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def option_parser(result)
|
48
|
+
OptionParser.new do |parser|
|
49
|
+
MAIN_PARAMS.each do |param|
|
50
|
+
parser.on("--#{param.downcase}=#{param}") do |pr|
|
51
|
+
result[param.downcase] = pr
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
ADDITIONAL_PARAMS.each do |param|
|
56
|
+
parser.on("--#{param.downcase}=#{param}") do |pr|
|
57
|
+
params = param.split('_')
|
58
|
+
|
59
|
+
if params.length == 2
|
60
|
+
result[params[0].downcase][params[1].downcase] = pr
|
61
|
+
elsif params.length == 1
|
62
|
+
if params[0].downcase == 'settings'
|
63
|
+
result[params[0].downcase] = parse_settings(pr)
|
64
|
+
else
|
65
|
+
result[params[0].downcase] = pr
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end.parse!
|
71
|
+
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
36
75
|
def load_from_env
|
37
76
|
log.debug "will load config from ENV variables"
|
38
77
|
|
@@ -54,7 +93,11 @@ module Config
|
|
54
93
|
if vars.length == 2
|
55
94
|
result[vars[0].downcase][vars[1].downcase] = ENV[var]
|
56
95
|
elsif vars.length == 1
|
57
|
-
|
96
|
+
if vars[0].downcase == 'settings'
|
97
|
+
result[vars[0].downcase] = parse_settings(ENV[var])
|
98
|
+
else
|
99
|
+
result[vars[0].downcase] = ENV[var]
|
100
|
+
end
|
58
101
|
end
|
59
102
|
end
|
60
103
|
end
|
@@ -69,36 +112,9 @@ module Config
|
|
69
112
|
log.debug "will load config from passed arguments"
|
70
113
|
result = make_default_config
|
71
114
|
|
72
|
-
|
73
|
-
MAIN_PARAMS.each do |param|
|
74
|
-
parser.on("--#{param.downcase}=#{param}") do |pr|
|
75
|
-
result[param.downcase] = pr
|
76
|
-
end
|
77
|
-
end
|
115
|
+
result = option_parser(result)
|
78
116
|
|
79
|
-
|
80
|
-
parser.on("--#{param.downcase}=#{param}") do |pr|
|
81
|
-
params = param.split('_')
|
82
|
-
|
83
|
-
if vars.length == 2
|
84
|
-
result[params[0].downcase][params[1].downcase] = pr
|
85
|
-
elsif vars.length == 1
|
86
|
-
result[params[0].downcase] = pr
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
begin
|
93
|
-
optparse.parse!
|
94
|
-
|
95
|
-
mandatory = MAIN_PARAMS.map { |p| p.downcase }
|
96
|
-
if mandatory.map { |key| result[key].empty? }.any?{ |a| a == true }
|
97
|
-
raise OptionParser::MissingArgument.new(mandatory.join(', '))
|
98
|
-
end
|
99
|
-
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
100
|
-
# puts $!.to_s
|
101
|
-
# puts optparse
|
117
|
+
if MAIN_PARAMS.map { |p| p.downcase }.map { |key| result[key].empty? }.any?{ |a| a == true }
|
102
118
|
log.fatal BANNER_ARGV
|
103
119
|
exit 1
|
104
120
|
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'http'
|
2
|
+
require 'yajl'
|
3
|
+
require 'elastic_manager/logger'
|
4
|
+
require 'elastic_manager/utils'
|
5
|
+
|
6
|
+
module Request
|
7
|
+
class Error < StandardError; end
|
8
|
+
class Throttling < Error; end
|
9
|
+
class ServerError < Error; end
|
10
|
+
|
11
|
+
class Elastic
|
12
|
+
include Logging
|
13
|
+
include Utils
|
14
|
+
|
15
|
+
RETRY_ERRORS = [StandardError, RuntimeError, Throttling]
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
@client = HTTP.timeout(
|
19
|
+
write: config['timeout']['write'].to_i,
|
20
|
+
connect: config['timeout']['connect'].to_i,
|
21
|
+
read: config['timeout']['read'].to_i
|
22
|
+
).headers(
|
23
|
+
'Accept': 'application/json',
|
24
|
+
'Content-type': 'application/json'
|
25
|
+
)
|
26
|
+
@url = config['es']['url']
|
27
|
+
@retry = config['retry'].to_i
|
28
|
+
@sleep = config['sleep'].to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_retry
|
32
|
+
tries ||= @retry
|
33
|
+
|
34
|
+
yield
|
35
|
+
|
36
|
+
rescue *RETRY_ERRORS => e
|
37
|
+
log.warn "tries left #{tries + 1} '''#{e.message}''' sleeping #{@sleep} sec..."
|
38
|
+
sleep @sleep
|
39
|
+
|
40
|
+
retry unless (tries -= 1).zero?
|
41
|
+
log.fatal "backtrace:\n\t#{e.backtrace.join("\n\t")}"
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def request(method, url, body={})
|
46
|
+
uri = @url + url
|
47
|
+
log.debug "uri: #{uri}"
|
48
|
+
|
49
|
+
with_retry do
|
50
|
+
response = @client.request(method, uri, json: body)
|
51
|
+
|
52
|
+
if response.code == 503
|
53
|
+
raise Request::Throttling.new(response)
|
54
|
+
elsif response.status.server_error?
|
55
|
+
raise Request::ServerError.new(response)
|
56
|
+
end
|
57
|
+
|
58
|
+
response
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def green?
|
63
|
+
response = request(:get, '/_cluster/health')
|
64
|
+
return json_parse(response)['status'] == 'green' if response.code == 200
|
65
|
+
false
|
66
|
+
end
|
67
|
+
|
68
|
+
def all_indices(from=nil, to=nil, state=nil, type=nil)
|
69
|
+
indices = get_all_indices
|
70
|
+
|
71
|
+
# TODO (anton.ryabov): next line just for debug purpose, need better handling
|
72
|
+
indices.each { |k, v| log.debug "#{k} - #{v.to_json}" unless v['settings'] }
|
73
|
+
|
74
|
+
indices.select!{ |_, v| v['state'] == state } if state
|
75
|
+
indices.select!{ |_, v| v['settings']['index']['routing']['allocation']['require']['box_type'] == type } if type
|
76
|
+
|
77
|
+
indices.map do |index, _|
|
78
|
+
begin
|
79
|
+
index_date = Date.parse(index.gsub('-', ''))
|
80
|
+
rescue ArgumentError => e
|
81
|
+
log.error "#{e.message} for #{index}"
|
82
|
+
next
|
83
|
+
end
|
84
|
+
|
85
|
+
URI.escape(index) if (from..to).cover? index_date
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_all_indices
|
90
|
+
req_path = '/_cluster/state/metadata/'
|
91
|
+
req_params = '?filter_path=metadata.indices.*.state,'
|
92
|
+
req_params << 'metadata.indices.*.settings.index.routing.allocation.require.box_type'
|
93
|
+
|
94
|
+
response = request(:get, req_path + req_params)
|
95
|
+
|
96
|
+
if response.code == 200
|
97
|
+
return json_parse(response)['metadata']['indices']
|
98
|
+
else
|
99
|
+
log.fatal "can't work with all_indices response was: #{response.code} - #{response}"
|
100
|
+
exit 1
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def find_snapshot_repo
|
105
|
+
# TODO: we need improve this if several snapshot repos used in elastic
|
106
|
+
response = request(:get, '/_snapshot/')
|
107
|
+
|
108
|
+
if response.code == 200
|
109
|
+
json_parse(response).keys.first
|
110
|
+
else
|
111
|
+
log.fatal "dunno what to do with: #{response.code} - #{response}"
|
112
|
+
exit 1
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def find_snapshot(repo, snapshot_name)
|
117
|
+
response = request(:get, "/_snapshot/#{repo}/#{snapshot_name}/")
|
118
|
+
|
119
|
+
if response.code == 200
|
120
|
+
snapshot = json_parse(response)['snapshots']
|
121
|
+
|
122
|
+
if snapshot.size == 1
|
123
|
+
snapshot.first['snapshot']
|
124
|
+
else
|
125
|
+
log.fatal "wrong snapshot size"
|
126
|
+
exit 1
|
127
|
+
end
|
128
|
+
else
|
129
|
+
log.fatal "can't find snapshot #{snapshot_name} in #{repo} response was: #{response.code} - #{response}"
|
130
|
+
exit 1
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def restore_snapshot(index)
|
135
|
+
snapshot_name = "snapshot_#{index}"
|
136
|
+
snapshot_repo = find_snapshot_repo
|
137
|
+
snapshot = find_snapshot(snapshot_repo, snapshot_name)
|
138
|
+
|
139
|
+
body = {
|
140
|
+
index_settings: {
|
141
|
+
'index.number_of_replicas' => 0,
|
142
|
+
'index.refresh_interval' => -1,
|
143
|
+
'index.routing.allocation.require.box_type' => 'warm'
|
144
|
+
}
|
145
|
+
}
|
146
|
+
response = request(:post, "/_snapshot/#{snapshot_repo}/#{snapshot}/_restore", body)
|
147
|
+
|
148
|
+
if response.code == 200
|
149
|
+
sleep 5
|
150
|
+
wait_snapshot_restore(index)
|
151
|
+
else
|
152
|
+
log.fatal "can't restore snapshot #{snapshot_name} response was: #{response.code} - #{response}"
|
153
|
+
exit 1
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def wait_snapshot_restore(index)
|
158
|
+
restore_ok = false
|
159
|
+
|
160
|
+
until restore_ok
|
161
|
+
sleep @sleep / 2
|
162
|
+
response = request(:get, "/#{index}/_recovery")
|
163
|
+
|
164
|
+
if response.code == 200
|
165
|
+
# TODO anton.ryabov: add logging of percent and time ?
|
166
|
+
restore_ok = json_parse(response)[index]['shards'].map { |s| s['stage'] == 'DONE' }.all?{ |a| a == true }
|
167
|
+
else
|
168
|
+
log.error "can't check recovery: #{response.code} - #{response}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
true
|
173
|
+
end
|
174
|
+
|
175
|
+
def open_index(index)
|
176
|
+
response = request(:post, "/#{index}/_open?master_timeout=1m")
|
177
|
+
|
178
|
+
if response.code == 200
|
179
|
+
response = json_parse(response)
|
180
|
+
else
|
181
|
+
log.fatal "wrong response code for #{index} open"
|
182
|
+
exit 1
|
183
|
+
end
|
184
|
+
|
185
|
+
return response['acknowledged'] == true
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yajl'
|
3
|
+
|
4
|
+
module Utils
|
5
|
+
include Logging
|
6
|
+
|
7
|
+
def true?(obj)
|
8
|
+
obj.to_s.downcase == 'true'
|
9
|
+
end
|
10
|
+
|
11
|
+
def json_parse(string)
|
12
|
+
JSON.parse(string)
|
13
|
+
rescue JSON::ParserError => e
|
14
|
+
log.fatal "json parse err: '''#{e.message}'''\n\t#{e.backtrace.join("\n\t")}"
|
15
|
+
exit 1
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elastic_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Antony Ryabov
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '12.3'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '12.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yajl-ruby
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.4'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.4'
|
55
83
|
description: Manager for logstash indices in elastic
|
56
84
|
email: mail@doam.ru
|
57
85
|
executables:
|
@@ -69,6 +97,7 @@ files:
|
|
69
97
|
- lib/elastic_manager/config.rb
|
70
98
|
- lib/elastic_manager/logger.rb
|
71
99
|
- lib/elastic_manager/request.rb
|
100
|
+
- lib/elastic_manager/utils.rb
|
72
101
|
homepage: https://github.com/onetwotrip/elastic_manager
|
73
102
|
licenses:
|
74
103
|
- MIT
|
@@ -89,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
118
|
version: '0'
|
90
119
|
requirements: []
|
91
120
|
rubyforge_project:
|
92
|
-
rubygems_version: 2.7.
|
121
|
+
rubygems_version: 2.7.7
|
93
122
|
signing_key:
|
94
123
|
specification_version: 4
|
95
124
|
summary: Because qurator sucks
|