elastic_manager 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/onetwotrip/elastic_manager.svg?branch=master)](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
|