elastic_manager 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5905bd89d21941b7ce9443d94d601c73fc035e038f7f7ab6ce570d94d1f9feb3
4
- data.tar.gz: 4eb528d29478710268b64ee7cba733fe885f6e1681c9aedf619491ba4fadcedf
3
+ metadata.gz: 7c798157f6af433e26a76c9935307a298d7d6fa4d1e164cb4234f0a144438586
4
+ data.tar.gz: a26879da569ed538f6320dc56cb9e2944aedcf6bf30a4850bfee97ab40c13ce6
5
5
  SHA512:
6
- metadata.gz: b46d51004f9b1bc3fd5521f119783179cf13ebe8a90ff3867ce71aedb3e4650bfa23e91039504e3a7935bfe702ee545f8f4885f9884e220419cd87ca9855b4de
7
- data.tar.gz: bd2764cba6ec941db093e45cf03e9a6a5b09b5c3c04fe226d8e628b36b38c08f55005f3931c4092da014699ea074426cdb54261eebf302a42baf91637632d38d
6
+ metadata.gz: 5f6f5cf3017e6bc33acd2ecf5b2eec443e17f37aa08681cefb30861fb5c95eaba00d229ed862f4ecc8549387f0de6bd9a47ebc79085ae85b3d1901d851b41dd1
7
+ data.tar.gz: c05c07cdaad9135cc47a5c2b25696d4ebb135f824ed42251d3c156dcd275e552cf135b98e85cba62d5a7f34029433233963a1a8b0b3e58e66bab8f18a91494b0
data/README.md CHANGED
@@ -4,6 +4,12 @@ Manager for logstash indices in elasticsearch. Why? Because qurator sucks!
4
4
 
5
5
  [![Build Status](https://travis-ci.org/onetwotrip/elastic_manager.svg?branch=master)](https://travis-ci.org/onetwotrip/elastic_manager)
6
6
 
7
+ Use ENV variables for run. Required variables:
8
+
9
+ - TASK
10
+ - INDICES
11
+ - FROM-TO or DAYSAGO (FROM-TO pair have precedence over DAYSAGO)
12
+
7
13
  Progress:
8
14
 
9
15
  - [x] Open closed indices
@@ -11,7 +17,10 @@ Progress:
11
17
  - [x] Open by date from and to
12
18
  - [x] Open by daysago
13
19
  - [x] Open all indices
14
- - [ ] Close indices
15
- - [ ] Chill indices
16
- - [ ] Snapshot indices
17
- - [ ] Delete snapshots
20
+ - [x] Close indices
21
+ - [x] Chill indices
22
+ - [x] Delete indices
23
+ - [x] Snapshot indices
24
+ - [x] Delete snapshots
25
+ - [x] Override daysago for concrete index
26
+ - [x] Skip any task for specific index
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Gem::Specification.new do |s|
2
4
  s.name = 'elastic_manager'
3
5
  s.executables = ['elastic_manager']
4
- s.version = '0.1.7'
6
+ s.version = '0.1.8'
5
7
  s.date = '2018-10-15'
6
8
  s.summary = 'Because qurator sucks'
7
9
  s.description = 'Manager for logstash indices in elastic'
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elastic_manager/logger'
4
+
5
+ # Index chilling operations
6
+ module Chill
7
+ include Logging
8
+
9
+ def chill_populate_indices(indices, date_from, date_to, daysago)
10
+ result = []
11
+
12
+ if indices.length == 1 && indices.first == '_all'
13
+ result = @elastic.all_indices(date_from, date_to, daysago, 'open', @config['settings']['box_types']['ingest'], @config['settings']['indices'])
14
+ else
15
+ if date_from.nil?
16
+ result = @elastic.all_indices(date_from, date_to, daysago, 'open', @config['settings']['box_types']['ingest'], @config['settings']['indices']).select { |r| r.start_with?(*indices) }
17
+ else
18
+ date_from.upto(date_to) do |date|
19
+ indices.each do |index|
20
+ date_formatted = date.to_s.tr('-', '.')
21
+ result << "#{index}-#{date_formatted}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ return result unless result.empty?
28
+
29
+ log.fatal 'no indices for work'
30
+ exit 1
31
+ end
32
+
33
+ def do_chill(indices)
34
+ indices.each do |index|
35
+ next if skip_index?(index, 'chill')
36
+
37
+ response = @elastic.request(:get, "/_cat/indices/#{index}")
38
+
39
+ if index_exist?(response)
40
+ next if already?(response, @config['settings']['box_types']['store'])
41
+
42
+ elastic_action_with_log('chill_index', index, @config['settings']['box_types']['store'])
43
+ else
44
+ log.warn "#{index} index not found"
45
+ end
46
+ end
47
+ end
48
+
49
+ def chill
50
+ indices, date_from, date_to, daysago = prepare_vars
51
+ prechecks(date_from, date_to)
52
+ indices = chill_populate_indices(indices, date_from, date_to, daysago)
53
+ log.debug indices.inspect
54
+ do_chill(indices)
55
+ end
56
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elastic_manager/logger'
4
+
5
+ # Index closing operations
6
+ module Close
7
+ include Logging
8
+
9
+ def close_populate_indices(indices, date_from, date_to, daysago)
10
+ result = []
11
+
12
+ if indices.length == 1 && indices.first == '_all'
13
+ result = @elastic.all_indices(date_from, date_to, daysago, 'open', nil, @config['settings']['indices'])
14
+ else
15
+ if date_from.nil?
16
+ result = @elastic.all_indices(date_from, date_to, daysago, 'open', nil, @config['settings']['indices']).select { |r| r.start_with?(*indices) }
17
+ else
18
+ date_from.upto(date_to) do |date|
19
+ indices.each do |index|
20
+ date_formatted = date.to_s.tr('-', '.')
21
+ result << "#{index}-#{date_formatted}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ return result unless result.empty?
28
+
29
+ log.fatal 'no indices for work'
30
+ exit 1
31
+ end
32
+
33
+ def do_close(indices)
34
+ indices.each do |index|
35
+ next if skip_index?(index, 'close')
36
+
37
+ response = @elastic.request(:get, "/_cat/indices/#{index}")
38
+
39
+ if index_exist?(response)
40
+ next if already?(response, 'close')
41
+
42
+ elastic_action_with_log('close_index', index, @config['settings']['box_types']['ingest'])
43
+ else
44
+ log.warn "#{index} index not found"
45
+ end
46
+ end
47
+ end
48
+
49
+ def close
50
+ indices, date_from, date_to, daysago = prepare_vars
51
+ prechecks(date_from, date_to)
52
+ indices = close_populate_indices(indices, date_from, date_to, daysago)
53
+ log.debug indices.inspect
54
+ do_close(indices)
55
+ end
56
+ end
@@ -35,15 +35,23 @@ module Config
35
35
  default['timeout']['connect'] = '3'
36
36
  default['timeout']['read'] = '120'
37
37
  default['daysago'] = ''
38
- default['settings'] = {}
38
+ default['settings'] = {
39
+ 'box_types' => {
40
+ 'ingest' => 'hot',
41
+ 'store' => 'warm'
42
+ },
43
+ 'indices' => {}
44
+ }
39
45
 
40
46
  log.debug "default config: #{default.inspect}"
41
47
  default
42
48
  end
43
49
 
44
- def check_settings(var)
50
+ def check_settings(var, config)
45
51
  if var.casecmp('settings').zero?
46
- json_parse(ENV[var])
52
+ env_settings = json_parse(ENV[var])
53
+ log.debug "env settings: #{env_settings}"
54
+ config['settings'].merge(env_settings)
47
55
  else
48
56
  ENV[var]
49
57
  end
@@ -58,20 +66,16 @@ module Config
58
66
  if vars.length == 2
59
67
  config[vars[0].downcase][vars[1].downcase] = ENV[var]
60
68
  elsif vars.length == 1
61
- config[vars[0].downcase] = check_settings(vars[0])
69
+ config[vars[0].downcase] = check_settings(vars[0], config)
62
70
  end
63
71
  end
64
72
 
65
73
  config
66
74
  end
67
75
 
68
- # def present?
69
- # !blank?
70
- # end
71
-
72
- def exit_if_invalid(config)
76
+ def validate_config(config)
73
77
  if config['task'].empty? || config['indices'].empty?
74
- fail_and_exit('not enough env variables. TASK, INDICES')
78
+ fail_and_exit('not enough env variables: TASK, INDICES')
75
79
  end
76
80
 
77
81
  if !config['from'].empty? && !config['to'].empty?
@@ -81,19 +85,19 @@ module Config
81
85
  rescue ArgumentError => e
82
86
  fail_and_exit("can't parse date #{key}: #{e.message}")
83
87
  end
84
- elsif config['from'].empty? || config['to'].empty?
85
- fail_and_exit('not enough env variables. FROM/TO or DAYSAGO')
88
+ elsif config['from'].empty? && !config['to'].empty?
89
+ fail_and_exit('not enough env variables: FROM')
90
+ elsif !config['from'].empty? && config['to'].empty?
91
+ fail_and_exit('not enough env variables: TO')
86
92
  elsif !config['daysago'].empty?
87
93
  log.debug 'will use daysago'
88
94
  config['from'], config['to'] = nil
89
95
  config['daysago'] = config['daysago'].to_i
90
96
  else
91
- fail_and_exit('not enough env variables. FROM/TO or DAYSAGO')
97
+ fail_and_exit('not enough env variables: FROM-TO or DAYSAGO')
92
98
  end
93
99
 
94
- # unless (!config['from'].empty? && !config['to'].empty?) || !config['daysago'].empty?
95
- # fail_and_exit('not enough env variables. FROM/TO or DAYSAGO')
96
- # end
100
+ config
97
101
  end
98
102
 
99
103
  def load_from_env
@@ -101,7 +105,7 @@ module Config
101
105
 
102
106
  config = make_default_config
103
107
  config = env_parser(config)
104
- exit_if_invalid(config)
108
+ config = validate_config(config)
105
109
 
106
110
  log.debug "env config: #{config.inspect}"
107
111
  config
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elastic_manager/logger'
4
+
5
+ # Index deleting operations
6
+ module Delete
7
+ include Logging
8
+
9
+ def delete_populate_indices(indices, date_from, date_to, daysago)
10
+ result = []
11
+
12
+ if indices.length == 1 && indices.first == '_all'
13
+ result = @elastic.all_indices(date_from, date_to, daysago, nil, nil, @config['settings']['indices'])
14
+ else
15
+ if date_from.nil?
16
+ result = @elastic.all_indices(date_from, date_to, daysago, nil, nil, @config['settings']['indices']).select { |r| r.start_with?(*indices) }
17
+ else
18
+ date_from.upto(date_to) do |date|
19
+ indices.each do |index|
20
+ date_formatted = date.to_s.tr('-', '.')
21
+ result << "#{index}-#{date_formatted}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ return result unless result.empty?
28
+
29
+ log.fatal 'no indices for work'
30
+ exit 1
31
+ end
32
+
33
+ def do_delete(indices)
34
+ indices.each do |index|
35
+ next if skip_index?(index, 'delete')
36
+
37
+ response = @elastic.request(:get, "/_cat/indices/#{index}")
38
+
39
+ if index_exist?(response)
40
+ elastic_action_with_log('delete_index', index)
41
+ else
42
+ log.warn "#{index} index not found"
43
+ end
44
+ end
45
+ end
46
+
47
+ def delete
48
+ indices, date_from, date_to, daysago = prepare_vars
49
+ prechecks(date_from, date_to)
50
+ indices = delete_populate_indices(indices, date_from, date_to, daysago)
51
+ log.debug indices.inspect
52
+ do_delete(indices)
53
+ end
54
+ end
@@ -5,9 +5,8 @@ require 'colorize'
5
5
 
6
6
  # Universal global logging
7
7
  module Logging
8
-
9
8
  SEVERITY_COLORS = {
10
- 'DEBUG' => 'cyan',
9
+ 'DEBUG' => 'white',
11
10
  'INFO' => 'green',
12
11
  'WARN' => 'yellow',
13
12
  'ERROR' => 'light_red',
@@ -6,89 +6,24 @@ require 'elastic_manager/logger'
6
6
  module Open
7
7
  include Logging
8
8
 
9
- def open_prechecks(date_from, date_to)
10
- unless date_from.nil?
11
- if date_from > date_to
12
- log.fatal "wrong dates: date to is behind date from. from: #{date_from}, to: #{date_to}"
13
- exit 1
14
- end
15
- end
16
-
17
- unless true?(@config['force'])
18
- unless @elastic.green?
19
- fail_and_exit("elasticsearch on #{@config['es']['url']} is not green")
20
- end
21
- end
22
- end
23
-
24
- def skip_open?(index)
25
- index_name = index.split('-')[0..-2].join('-')
26
-
27
- if @config['settings'][index_name] && @config['settings'][index_name]['skip_open']
28
- if true?(@config['settings'][index_name]['skip_open'])
29
- log.warn "#{index_name} index open skiped"
30
- return true
31
- end
32
- end
33
-
34
- false
35
- end
36
-
37
- def index_exist?(response)
38
- if response.code == 200
39
- true
40
- elsif response.code == 404
41
- false
42
- else
43
- log.fatal "wtf in index_exist? response was: #{response.code} - #{response}"
44
- exit 1
45
- end
46
- end
47
-
48
- def already_open?(response)
49
- index = json_parse(response).first
50
- if index['status'] == 'open'
51
- log.warn "#{index['index']} index status already open"
52
- return true
53
- end
54
-
55
- false
56
- end
57
-
58
- def open_prepare_vars
59
- indices = @config['indices'].split(',')
60
- daysago = @config['daysago']
61
- date_from = @config['from']
62
- date_to = @config['to']
63
-
64
- [indices, date_from, date_to, daysago]
65
- end
66
-
67
- def action_with_log(action, index)
68
- if @elastic.send(action, index)
69
- log.info "#{index} #{action} succes"
70
- else
71
- log.error "#{index} #{action} fail"
72
- end
73
- end
74
-
75
- def populate_indices(indices, date_from, date_to, daysago)
9
+ def open_populate_indices(indices, date_from, date_to, daysago)
76
10
  result = []
77
11
 
78
12
  if indices.length == 1 && indices.first == '_all'
79
- result = @elastic.all_indices(date_from, date_to, daysago, 'close')
80
- result += @elastic.all_indices_in_snapshots(date_from, date_to, daysago)
13
+ result = @elastic.all_indices(date_from, date_to, daysago, 'close', nil, @config['settings']['indices'])
14
+ result += @elastic.all_indices_in_snapshots(date_from, date_to, daysago, @config['settings']['indices'])
81
15
  return result
82
16
  end
83
17
 
84
18
  if date_from.nil?
85
- result = @elastic.all_indices(date_from, date_to, daysago, 'close')
86
- result += @elastic.all_indices_in_snapshots(date_from, date_to, daysago)
19
+ result = @elastic.all_indices(date_from, date_to, daysago, 'close', nil, @config['settings']['indices'])
20
+ result += @elastic.all_indices_in_snapshots(date_from, date_to, daysago, @config['settings']['indices'])
87
21
  return result.select { |r| r.start_with?(*indices) }
88
22
  else
89
23
  date_from.upto(date_to) do |date|
90
24
  indices.each do |index|
91
- result << "#{index}-#{date.to_s.tr!('-', '.')}"
25
+ date_formatted = date.to_s.tr('-', '.')
26
+ result << "#{index}-#{date_formatted}"
92
27
  end
93
28
  end
94
29
  end
@@ -101,30 +36,28 @@ module Open
101
36
 
102
37
  def do_open(indices)
103
38
  indices.each do |index|
104
- next if skip_open?(index)
39
+ next if skip_index?(index, 'open')
105
40
 
106
41
  response = @elastic.request(:get, "/_cat/indices/#{index}")
107
42
 
108
43
  if index_exist?(response)
109
- next if already_open?(response)
44
+ next if already?(response, 'open')
110
45
 
111
- action_with_log('open_index', index)
46
+ elastic_action_with_log('open_index', index)
112
47
  else
113
48
  log.warn "#{index} index not found"
114
49
  log.info "#{index} trying snapshot restore"
115
50
 
116
- action_with_log('restore_snapshot', index)
51
+ elastic_action_with_log('restore_snapshot', index, @config['settings']['box_types']['store'])
117
52
  end
118
53
  end
119
54
  end
120
55
 
121
56
  def open
122
- indices, date_from, date_to, daysago = open_prepare_vars
123
- open_prechecks(date_from, date_to)
124
- indices = populate_indices(indices, date_from, date_to, daysago)
125
-
57
+ indices, date_from, date_to, daysago = prepare_vars
58
+ prechecks(date_from, date_to)
59
+ indices = open_populate_indices(indices, date_from, date_to, daysago)
126
60
  log.debug indices.inspect
127
-
128
61
  do_open(indices)
129
62
  end
130
63
  end
@@ -37,7 +37,6 @@ module Request
37
37
  tries ||= @retry
38
38
 
39
39
  yield
40
-
41
40
  rescue *RETRY_ERRORS => e
42
41
  log.warn "tries left #{tries + 1} '''#{e.message}''' sleeping #{@sleep} sec..."
43
42
  sleep @sleep
@@ -71,23 +70,36 @@ module Request
71
70
  false
72
71
  end
73
72
 
74
- def all_indices_in_snapshots(from=nil, to=nil, daysago=nil)
73
+ def override_daysago(index_name, indices_config, daysago)
74
+ if indices_config[index_name] &&
75
+ indices_config[index_name]['daysago'] &&
76
+ !indices_config[index_name]['daysago'].to_s.empty?
77
+ indices_config[index_name]['daysago']
78
+ else
79
+ daysago
80
+ end
81
+ end
82
+
83
+ def all_indices_in_snapshots(from=nil, to=nil, daysago=nil, indices_config)
75
84
  all_snapshots = get_all_snapshots
76
85
  all_snapshots.select! { |snap| snap['status'] == 'SUCCESS' }
77
86
 
78
87
  result = []
79
88
  all_snapshots.each do |snap|
80
89
  begin
81
- snap_date = Date.parse(snap['id'].gsub('-', ''))
90
+ snap_date = Date.parse(snap['id'].delete('-'))
82
91
  rescue ArgumentError => e
83
92
  log.error "#{e.message} for #{index}"
84
93
  next
85
94
  end
86
95
 
87
- if from.nil? && snap_date < (Date.today - daysago)
88
- result << CGI.escape(snap['id'].gsub('snapshot_', ''))
96
+ index = snap['id'].gsub('snapshot_', '')
97
+ daysago_local = override_daysago(make_index_name(index), indices_config, daysago)
98
+
99
+ if from.nil? && snap_date < (Date.today - daysago_local)
100
+ result << CGI.escape(index)
89
101
  elsif (from..to).cover? snap_date
90
- result << CGI.escape(snap['id'].gsub('snapshot_', ''))
102
+ result << CGI.escape(index)
91
103
  end
92
104
  end
93
105
 
@@ -106,7 +118,7 @@ module Request
106
118
  end
107
119
  end
108
120
 
109
- def all_indices(from=nil, to=nil, daysago=nil, state=nil, type=nil)
121
+ def all_indices(from=nil, to=nil, daysago=nil, state=nil, type=nil, indices_config)
110
122
  indices = get_all_indices
111
123
 
112
124
  # TODO: (anton.ryabov) next line just for debug purpose, need better handling
@@ -118,13 +130,15 @@ module Request
118
130
  result = []
119
131
  indices.each do |index, _|
120
132
  begin
121
- index_date = Date.parse(index.gsub('-', ''))
133
+ index_date = Date.parse(index.delete('-'))
122
134
  rescue ArgumentError => e
123
135
  log.error "#{e.message} for #{index}"
124
136
  next
125
137
  end
126
138
 
127
- if from.nil? && index_date < (Date.today - daysago)
139
+ daysago_local = override_daysago(make_index_name(index), indices_config, daysago)
140
+
141
+ if from.nil? && index_date < (Date.today - daysago_local)
128
142
  result << CGI.escape(index)
129
143
  elsif (from..to).cover? index_date
130
144
  result << CGI.escape(index)
@@ -149,6 +163,19 @@ module Request
149
163
  end
150
164
  end
151
165
 
166
+ def snapshot_exist?(snapshot_name, repo)
167
+ response = request(:get, "/_snapshot/#{repo}/#{snapshot_name}/")
168
+
169
+ if response.code == 200
170
+ true
171
+ elsif response.code == 404
172
+ false
173
+ else
174
+ log.fatal "can't check snapshot existing, response was: #{response.code} - #{response}"
175
+ exit 1
176
+ end
177
+ end
178
+
152
179
  def find_snapshot_repo
153
180
  # TODO: we need improve this if several snapshot repos used in elastic
154
181
  response = request(:get, '/_snapshot/')
@@ -165,12 +192,12 @@ module Request
165
192
  response = request(:get, "/_snapshot/#{repo}/#{snapshot_name}/")
166
193
 
167
194
  if response.code == 200
168
- snapshot = json_parse(response)['snapshots']
195
+ snapshot = json_parse(response)['snapshots'][0]
169
196
 
170
- if snapshot.size == 1
171
- snapshot.first['snapshot']
197
+ if snapshot['state'] == 'SUCCESS'
198
+ snapshot['snapshot']
172
199
  else
173
- log.fatal "wrong snapshot size"
200
+ log.fatal 'wrong snapshot state'
174
201
  exit 1
175
202
  end
176
203
  else
@@ -179,7 +206,7 @@ module Request
179
206
  end
180
207
  end
181
208
 
182
- def restore_snapshot(index)
209
+ def restore_snapshot(index, box_type)
183
210
  snapshot_name = "snapshot_#{index}"
184
211
  snapshot_repo = find_snapshot_repo
185
212
  snapshot = find_snapshot(snapshot_repo, snapshot_name)
@@ -188,7 +215,7 @@ module Request
188
215
  index_settings: {
189
216
  'index.number_of_replicas' => 0,
190
217
  'index.refresh_interval' => -1,
191
- 'index.routing.allocation.require.box_type' => 'warm'
218
+ 'index.routing.allocation.require.box_type' => box_type
192
219
  }
193
220
  }
194
221
  response = request(:post, "/_snapshot/#{snapshot_repo}/#{snapshot}/_restore", body)
@@ -227,7 +254,141 @@ module Request
227
254
  response = json_parse(response)
228
255
  else
229
256
  log.fatal "wrong response code for #{index} open"
230
- exit 1
257
+ return false
258
+ end
259
+
260
+ response['acknowledged'].is_a?(TrueClass)
261
+ end
262
+
263
+ def close_index(index, tag)
264
+ box_type = index_box_type(index)
265
+
266
+ return false if box_type.nil?
267
+
268
+ if box_type == tag
269
+ log.fatal "i will not close index #{index} in box_type #{tag}"
270
+ false
271
+ else
272
+ response = request(:post, "/#{index}/_close?master_timeout=1m")
273
+
274
+ if response.code == 200
275
+ response = json_parse(response)
276
+ else
277
+ log.fatal "wrong response code for #{index} close"
278
+ return false
279
+ end
280
+
281
+ response['acknowledged'].is_a?(TrueClass)
282
+ end
283
+ end
284
+
285
+ def index_box_type(index)
286
+ response = request(:get, "/#{index}/_settings/index.routing.allocation.require.box_type")
287
+
288
+ if response.code == 200
289
+ response = json_parse(response)
290
+ box_type = response[index]['settings']['index']['routing']['allocation']['require']['box_type']
291
+ log.debug "for #{index} box_type is #{box_type}"
292
+ box_type
293
+ else
294
+ log.fatal "can't check box_type for #{index}, response was: #{response.code} - #{response}"
295
+ nil
296
+ end
297
+ end
298
+
299
+ def chill_index(index, box_type)
300
+ body = {
301
+ 'index.routing.allocation.require.box_type' => box_type
302
+ }
303
+ response = request(:put, "/#{index}/_settings?master_timeout=1m", body)
304
+
305
+ if response.code == 200
306
+ response = json_parse(response)
307
+ else
308
+ log.fatal "can't chill #{index}, response was: #{response.code} - #{response}"
309
+ return false
310
+ end
311
+
312
+ response['acknowledged'].is_a?(TrueClass)
313
+ end
314
+
315
+ def delete_index(index)
316
+ snapshot_name = "snapshot_#{index}"
317
+ snapshot_repo = find_snapshot_repo
318
+
319
+ return false unless find_snapshot(snapshot_repo, snapshot_name)
320
+
321
+ response = request(:delete, "/#{index}")
322
+
323
+ if response.code == 200
324
+ response = json_parse(response)
325
+ else
326
+ log.fatal "can't delete index #{index}, response was: #{response.code} - #{response}"
327
+ return false
328
+ end
329
+
330
+ response['acknowledged'].is_a?(TrueClass)
331
+ end
332
+
333
+ def wait_snapshot(snapshot, repo)
334
+ snapshot_ok = false
335
+
336
+ until snapshot_ok
337
+ sleep @sleep
338
+ response = request(:get, "/_snapshot/#{repo}/#{snapshot}/_status")
339
+
340
+ if response.code == 200
341
+ # TODO: (anton.ryabov) add logging of percent and time ?
342
+ # stats = status['snapshots'][0]['stats']
343
+ # msg = "(#{stats['total_size_in_bytes']/1024/1024/1024}Gb / #{stats['processed_size_in_bytes']/1024/1024/1024}Gb)"
344
+ # puts "Get backup status #{msg}: retry attempt #{attempt_number}; #{total_delay.round} seconds have passed."
345
+ state = json_parse(response)['snapshots'][0]['state']
346
+ log.debug "snapshot check response: #{response.code} - #{response}"
347
+
348
+ if state == 'SUCCESS'
349
+ snapshot_ok = true
350
+ elsif %w[FAILED PARTIAL INCOMPATIBLE].include?(state)
351
+ log.fatal "can't snapshot #{snapshot} in #{repo}: #{response.code} - #{response}"
352
+ exit 1
353
+ end
354
+ else
355
+ log.error "can't check snapshot: #{response.code} - #{response}"
356
+ end
357
+ end
358
+
359
+ true
360
+ end
361
+
362
+ def snapshot_index(index)
363
+ snapshot_name = "snapshot_#{index}"
364
+ snapshot_repo = find_snapshot_repo
365
+
366
+ body = {
367
+ 'indices' => index,
368
+ 'ignore_unavailable' => false,
369
+ 'include_global_state' => false,
370
+ 'partial' => false
371
+ }
372
+
373
+ response = request(:put, "/_snapshot/#{snapshot_repo}/#{snapshot_name}/", body)
374
+
375
+ if response.code == 200
376
+ sleep 5
377
+ wait_snapshot(snapshot_name, snapshot_repo)
378
+ else
379
+ log.error "can't snapshot #{index}, response was: #{response.code} - #{response}"
380
+ false
381
+ end
382
+ end
383
+
384
+ def delete_snapshot(snapshot, repo)
385
+ response = request(:delete, "/_snapshot/#{repo}/#{snapshot}")
386
+
387
+ if response.code == 200
388
+ response = json_parse(response)
389
+ else
390
+ log.fatal "can't delete snapshot #{snapshot}, response was: #{response.code} - #{response}"
391
+ return false
231
392
  end
232
393
 
233
394
  response['acknowledged'].is_a?(TrueClass)
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elastic_manager/logger'
4
+
5
+ # Index snapshots deleting operations
6
+ module SnapDelete
7
+ include Logging
8
+
9
+ def snapdelete_populate_indices(indices, date_from, date_to, daysago)
10
+ result = []
11
+
12
+ if indices.length == 1 && indices.first == '_all'
13
+ result = @elastic.all_indices_in_snapshots(date_from, date_to, daysago, @config['settings']['indices'])
14
+ else
15
+ if date_from.nil?
16
+ result = @elastic.all_indices_in_snapshots(date_from, date_to, daysago, @config['settings']['indices']).select { |r| r.start_with?(*indices) }
17
+ else
18
+ date_from.upto(date_to) do |date|
19
+ indices.each do |index|
20
+ date_formatted = date.to_s.tr('-', '.')
21
+ result << "#{index}-#{date_formatted}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ return result unless result.empty?
28
+
29
+ log.fatal 'no indices for work'
30
+ exit 1
31
+ end
32
+
33
+ def do_snapdelete(indices)
34
+ indices.each do |index|
35
+ next if skip_index?(index, 'snapdelete')
36
+
37
+ snapshot_name = "snapshot_#{index}"
38
+ snapshot_repo = @elastic.find_snapshot_repo
39
+
40
+ if @elastic.snapshot_exist?(snapshot_name, snapshot_repo)
41
+ elastic_action_with_log('delete_snapshot', snapshot_name, snapshot_repo)
42
+ else
43
+ log.warn "#{index} snapshot #{snapshot_name} not found"
44
+ end
45
+ end
46
+ end
47
+
48
+ def snapdelete
49
+ indices, date_from, date_to, daysago = prepare_vars
50
+ prechecks(date_from, date_to)
51
+ indices = snapdelete_populate_indices(indices, date_from, date_to, daysago)
52
+ log.debug indices.inspect
53
+ do_snapdelete(indices)
54
+ end
55
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elastic_manager/logger'
4
+
5
+ # Index snapshoting operations
6
+ module Snapshot
7
+ include Logging
8
+
9
+ def snapshot_populate_indices(indices, date_from, date_to, daysago)
10
+ result = []
11
+
12
+ if indices.length == 1 && indices.first == '_all'
13
+ result = @elastic.all_indices(date_from, date_to, daysago, nil, @config['settings']['box_types']['store'], @config['settings']['indices'])
14
+ else
15
+ if date_from.nil?
16
+ result = @elastic.all_indices(date_from, date_to, daysago, nil, @config['settings']['box_types']['store'], @config['settings']['indices']).select { |r| r.start_with?(*indices) }
17
+ else
18
+ date_from.upto(date_to) do |date|
19
+ indices.each do |index|
20
+ date_formatted = date.to_s.tr('-', '.')
21
+ result << "#{index}-#{date_formatted}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ return result unless result.empty?
28
+
29
+ log.fatal 'no indices for work'
30
+ exit 1
31
+ end
32
+
33
+ def do_snapshot(indices)
34
+ indices.each do |index|
35
+ next if skip_index?(index, 'snapshot')
36
+
37
+ response = @elastic.request(:get, "/_cat/indices/#{index}")
38
+
39
+ if index_exist?(response)
40
+ elastic_action_with_log('open_index', index) unless already?(response, 'open')
41
+ elastic_action_with_log('delete_index', index) if elastic_action_with_log('snapshot_index', index)
42
+ else
43
+ log.warn "#{index} index not found"
44
+ end
45
+ end
46
+ end
47
+
48
+ def snapshot
49
+ indices, date_from, date_to, daysago = prepare_vars
50
+ prechecks(date_from, date_to)
51
+ indices = snapshot_populate_indices(indices, date_from, date_to, daysago)
52
+ log.debug indices.inspect
53
+ do_snapshot(indices)
54
+ end
55
+ end
@@ -22,4 +22,97 @@ module Utils
22
22
  log.fatal text
23
23
  exit 1
24
24
  end
25
+
26
+ def make_index_name(index)
27
+ index.split('-')[0..-2].join('-')
28
+ end
29
+
30
+ def skip_index?(index, state)
31
+ index_name = make_index_name(index)
32
+
33
+ if @config['settings']['indices'][index_name] &&
34
+ @config['settings']['indices'][index_name]['skip'] &&
35
+ @config['settings']['indices'][index_name]['skip'][state]
36
+
37
+ if true?(@config['settings']['indices'][index_name]['skip'][state])
38
+ log.warn "#{index_name} index #{state} skiped due settings"
39
+ return true
40
+ end
41
+ end
42
+
43
+ false
44
+ end
45
+
46
+ def prepare_vars
47
+ indices = @config['indices'].split(',')
48
+ daysago = @config['daysago']
49
+ date_from = @config['from']
50
+ date_to = @config['to']
51
+
52
+ [indices, date_from, date_to, daysago]
53
+ end
54
+
55
+ def prechecks(date_from, date_to)
56
+ unless date_from.nil?
57
+ if date_from > date_to
58
+ log.fatal "wrong dates: date to is behind date from. from: #{date_from}, to: #{date_to}"
59
+ exit 1
60
+ end
61
+ end
62
+
63
+ return if true?(@config['force'])
64
+ return if @elastic.green?
65
+
66
+ fail_and_exit("elasticsearch on #{@config['es']['url']} is not green")
67
+ end
68
+
69
+ def already?(response, state)
70
+ if %w[open close].include?(state)
71
+ index = json_parse(response).first
72
+ if index['status'] == state
73
+ log.warn "#{index['index']} index status already #{state}"
74
+ return true
75
+ end
76
+ elsif %w[hot warm].include?(state)
77
+ index = json_parse(response).first['index']
78
+ box_type = @elastic.index_box_type(index)
79
+
80
+ if box_type.nil?
81
+ log.info "can't get box_type, look at the previous error for info, will skip #{index}"
82
+ return true
83
+ end
84
+
85
+ if box_type == state
86
+ log.warn "#{index['index']} index already #{state}"
87
+ return true
88
+ end
89
+ else
90
+ log.fatal "wrong state for check #{state}, bad coder"
91
+ exit 1
92
+ end
93
+
94
+ false
95
+ end
96
+
97
+ def elastic_action_with_log(action, index, *params)
98
+ if @elastic.send(action, index, *params)
99
+ log.info "#{index} #{action} succes"
100
+ else
101
+ log.error "#{index} #{action} fail"
102
+ return false
103
+ end
104
+
105
+ true
106
+ end
107
+
108
+ def index_exist?(response)
109
+ if response.code == 200
110
+ true
111
+ elsif response.code == 404
112
+ false
113
+ else
114
+ log.fatal "wtf in index_exist? response was: #{response.code} - #{response}"
115
+ exit 1
116
+ end
117
+ end
25
118
  end
@@ -7,6 +7,11 @@ require 'elastic_manager/logger'
7
7
  require 'elastic_manager/request'
8
8
  require 'elastic_manager/utils'
9
9
  require 'elastic_manager/open'
10
+ require 'elastic_manager/close'
11
+ require 'elastic_manager/chill'
12
+ require 'elastic_manager/snapshot'
13
+ require 'elastic_manager/delete'
14
+ require 'elastic_manager/snapdelete'
10
15
 
11
16
  # Main
12
17
  class ElasticManager
@@ -15,6 +20,11 @@ class ElasticManager
15
20
  include Request
16
21
  include Utils
17
22
  include Open
23
+ include Close
24
+ include Chill
25
+ include Snapshot
26
+ include Delete
27
+ include SnapDelete
18
28
 
19
29
  def initialize
20
30
  @config = load_from_env
@@ -25,6 +35,18 @@ class ElasticManager
25
35
  def run
26
36
  if @config['task'].casecmp('open').zero?
27
37
  open
38
+ elsif @config['task'].casecmp('close').zero?
39
+ close
40
+ elsif @config['task'].casecmp('chill').zero?
41
+ chill
42
+ elsif @config['task'].casecmp('snapshot').zero?
43
+ snapshot
44
+ elsif @config['task'].casecmp('delete').zero?
45
+ delete
46
+ elsif @config['task'].casecmp('snapdelete').zero?
47
+ snapdelete
48
+ else
49
+ fail_and_exit('wrong task')
28
50
  end
29
51
  end
30
52
  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.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Antony Ryabov
@@ -94,10 +94,15 @@ files:
94
94
  - bin/elastic_manager
95
95
  - elastic_manager.gemspec
96
96
  - lib/elastic_manager.rb
97
+ - lib/elastic_manager/chill.rb
98
+ - lib/elastic_manager/close.rb
97
99
  - lib/elastic_manager/config.rb
100
+ - lib/elastic_manager/delete.rb
98
101
  - lib/elastic_manager/logger.rb
99
102
  - lib/elastic_manager/open.rb
100
103
  - lib/elastic_manager/request.rb
104
+ - lib/elastic_manager/snapdelete.rb
105
+ - lib/elastic_manager/snapshot.rb
101
106
  - lib/elastic_manager/utils.rb
102
107
  homepage: https://github.com/onetwotrip/elastic_manager
103
108
  licenses: