elastic_manager 0.1.7 → 0.1.8

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 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: