sgfa 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -31,25 +31,6 @@ class Binder < Thor
31
31
  desc: 'Path to the binder',
32
32
  }
33
33
 
34
- class_option :user, {
35
- type: :string,
36
- desc: 'User name.',
37
- default: Etc.getpwuid(Process.euid).name,
38
- }
39
-
40
- class_option :body, {
41
- type: :string,
42
- desc: 'Body of an entry.',
43
- default: '!! No body provided using CLI tool !!',
44
- }
45
-
46
- class_option :title, {
47
- type: :string,
48
- desc: 'Title of an entry.',
49
- default: '!! No title provided using CLI tool !!',
50
- }
51
-
52
-
53
34
 
54
35
  #####################################
55
36
  # info
@@ -57,17 +38,7 @@ class Binder < Thor
57
38
  def info
58
39
 
59
40
  # open binder
60
- if !options[:fs_path]
61
- puts 'Binder type and location required.'
62
- return
63
- end
64
- bnd = ::Sgfa::BinderFs.new
65
- begin
66
- bnd.open(options[:fs_path])
67
- rescue ::Sgfa::Error::Limits, ::Sgfa::Error::NonExistent => exp
68
- puts exp.message
69
- return
70
- end
41
+ return if !(bnd = _open_binder)
71
42
 
72
43
  # print info
73
44
  tr = {
@@ -87,6 +58,21 @@ class Binder < Thor
87
58
  #####################################
88
59
  # create
89
60
  desc 'create <id_text> <type_json>', 'Create a new Binder'
61
+ method_option :user, {
62
+ type: :string,
63
+ desc: 'User name.',
64
+ default: Etc.getpwuid(Process.euid).name,
65
+ }
66
+ method_option :body, {
67
+ type: :string,
68
+ desc: 'Body of an entry.',
69
+ default: '!! No body provided using CLI tool !!',
70
+ }
71
+ method_option :title, {
72
+ type: :string,
73
+ desc: 'Title of an entry.',
74
+ default: '!! No title provided using CLI tool !!',
75
+ }
90
76
  def create(id_text, init_json)
91
77
 
92
78
  if !options[:fs_path]
@@ -152,7 +138,238 @@ class Binder < Thor
152
138
  :BindAddress => options[:addr],
153
139
  })
154
140
 
155
- end # def web()
141
+ end # def web_demo()
142
+
143
+
144
+ #####################################
145
+ # Backup to AWS S3
146
+ desc 'backup_s3', 'Backup to AWS S3'
147
+ method_option :key, {
148
+ type: :string,
149
+ desc: 'Path of AWS credentials',
150
+ required: true,
151
+ }
152
+ method_option :bucket, {
153
+ type: :string,
154
+ desc: 'S3 bucket name',
155
+ required: true,
156
+ }
157
+ method_option :level, {
158
+ type: :string,
159
+ desc: 'Debug level, "debug", "info", "warn", "error"',
160
+ default: 'error',
161
+ }
162
+ method_option :last, {
163
+ desc: 'Last backup state file name',
164
+ type: :string,
165
+ }
166
+ method_option :save, {
167
+ desc: 'Save backup state to file name',
168
+ type: :string,
169
+ }
170
+ def backup_s3()
171
+
172
+ return if !(aws = _get_aws)
173
+ return if !(bnd = _open_binder)
174
+ log = _get_log
175
+ last = _get_last
176
+ s3 = ::Aws::S3::Client.new(aws)
177
+ sto = ::Sgfa::StoreS3.new
178
+ sto.open(s3, options[:bucket])
179
+ last = bnd.backup_push(sto, prev: last, log: log)
180
+ bnd.close
181
+ sto.close
182
+ _put_last(last)
183
+
184
+ end # def backup_s3()
185
+
186
+
187
+ #####################################
188
+ # Restore from AWS S3
189
+ desc 'restore_s3 <id_text>', 'Restore from AWS S3'
190
+ method_option :key, {
191
+ type: :string,
192
+ desc: 'Path of AWS credentials',
193
+ required: true,
194
+ }
195
+ method_option :bucket, {
196
+ type: :string,
197
+ desc: 'S3 bucket name',
198
+ required: true,
199
+ }
200
+ method_option :level, {
201
+ type: :string,
202
+ desc: 'Debug level, "debug", "info", "warn", "error"',
203
+ default: 'error',
204
+ }
205
+ def restore_s3(id_text)
206
+ if !options[:fs_path]
207
+ puts 'Binder type and location required.'
208
+ return
209
+ end
210
+ return if !(aws = _get_aws)
211
+ log = _get_log
212
+ s3 = ::Aws::S3::Client.new(aws)
213
+ sto = ::Sgfa::StoreS3.new
214
+ sto.open(s3, options[:bucket])
215
+ bnd = ::Sgfa::BinderFs.new
216
+ bnd.create_raw(options[:fs_path], id_text)
217
+ bnd.open(options[:fs_path])
218
+ bnd.backup_pull(sto, log: log)
219
+ bnd.close
220
+ sto.close
221
+ end # def restore_s3()
222
+
223
+
224
+ #####################################
225
+ # backup to filesystem
226
+ desc 'backup_fs <dir>', 'Backup to file system'
227
+ method_option :last, {
228
+ desc: 'Last backup state file name',
229
+ type: :string,
230
+ }
231
+ method_option :save, {
232
+ desc: 'Save backup state to file name',
233
+ type: :string,
234
+ }
235
+ method_option :level, {
236
+ type: :string,
237
+ desc: 'Debug level, "debug", "info", "warn", "error"',
238
+ default: 'error',
239
+ }
240
+ def backup_fs(dest)
241
+ return if !(bnd = _open_binder)
242
+ log = _get_log
243
+ last = _get_last
244
+ sto = ::Sgfa::StoreFs.new
245
+ sto.open(dest)
246
+ last = bnd.backup_push(sto, prev: last, log: log)
247
+ bnd.close
248
+ sto.close
249
+ _put_last(last)
250
+ end # def backup_fs()
251
+
252
+
253
+ #####################################
254
+ # Restore from filesystem
255
+ desc 'restore_fs <id_text> <backup_store>', 'Restore from file system'
256
+ method_option :level, {
257
+ type: :string,
258
+ desc: 'Debug level, "debug", "info", "warn", "error"',
259
+ default: 'error',
260
+ }
261
+ def restore_fs(id_text, bak)
262
+ if !options[:fs_path]
263
+ puts 'Binder type and location required.'
264
+ return
265
+ end
266
+ sto = ::Sgfa::StoreFs.new
267
+ sto.open(bak)
268
+ log = _get_log
269
+ bnd = ::Sgfa::BinderFs.new
270
+ bnd.create_raw(options[:fs_path], id_text)
271
+ bnd.open(options[:fs_path])
272
+ bnd.backup_pull(sto, log: log)
273
+ bnd.close
274
+ sto.close
275
+ end # def restore_fs()
276
+
277
+
278
+ no_commands do
279
+
280
+
281
+ # Open the binder
282
+ def _open_binder()
283
+ # open binder
284
+ if !options[:fs_path]
285
+ puts 'Binder type and location required.'
286
+ return false
287
+ end
288
+ bnd = ::Sgfa::BinderFs.new
289
+ begin
290
+ bnd.open(options[:fs_path])
291
+ rescue ::Sgfa::Error::Limits, ::Sgfa::Error::NonExistent => exp
292
+ puts exp.message
293
+ return false
294
+ end
295
+ return bnd
296
+ end # def _open_binder()
297
+
298
+
299
+ # get the log
300
+ def _get_log
301
+ log = Logger.new(STDOUT)
302
+ case options[:level]
303
+ when 'debug'
304
+ log.level = Logger::DEBUG
305
+ when 'info'
306
+ log.level = Logger::INFO
307
+ when 'warn'
308
+ log.level = Logger::WARN
309
+ else
310
+ log.level = Logger::ERROR
311
+ end
312
+ return log
313
+ end # def _get_log
314
+
315
+
316
+ # Get AWS creds
317
+ def _get_aws
318
+ json = File.read(options[:key])
319
+ creds = JSON.parse(json)
320
+ opts = {
321
+ region: creds['aws_region'],
322
+ access_key_id: creds['aws_id'],
323
+ secret_access_key: creds['aws_key'],
324
+ }
325
+ return opts
326
+
327
+ rescue Errno::ENOENT
328
+ puts 'AWS keys file not found'
329
+ return false
330
+
331
+ rescue Errno::EACCESS
332
+ puts 'AWS key file permission denied'
333
+ return false
334
+
335
+ rescue JSON::JSONError
336
+ puts 'AWS key file JSON parse error'
337
+ return false
338
+ end # def get_aws
339
+
340
+
341
+ # Get last option
342
+ def _get_last()
343
+ return {} if !options[:last]
344
+ json = File.read(options[:last])
345
+ last = JSON.parse(json)
346
+ return last
347
+ rescue Errno::ENOENT
348
+ puts "Last backup state file not found"
349
+ exit
350
+ rescue Errno::EACCESS
351
+ puts "Access denied to last backup state file"
352
+ exit
353
+ rescue JSON::JSONError
354
+ puts "Last backup state file parse failed"
355
+ exit
356
+ end # def _get_last()
357
+ private :_get_last
358
+
359
+
360
+ # Save last
361
+ def _put_last(last)
362
+ return if !options[:save]
363
+ json = JSON.pretty_generate(last) + "\n"
364
+ File.open(options[:save], 'w', :encoding => 'utf-8'){|fi| fi.write json}
365
+ rescue Errno::EACCESS
366
+ puts "Access denied to backup state file"
367
+ exit
368
+ end # def _put_last()
369
+ private :_put_last
370
+
371
+ end
372
+
156
373
 
157
374
  end # class Binder
158
375
 
@@ -9,9 +9,13 @@
9
9
  # This program is distributed WITHOUT ANY WARRANTY; without even the
10
10
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
11
 
12
+ require 'csv'
13
+
14
+ require 'aws-sdk'
12
15
  require 'thor'
13
16
 
14
17
  require_relative '../jacket_fs'
18
+ require_relative '../store_s3'
15
19
 
16
20
  module Sgfa
17
21
  module Cli
@@ -270,6 +274,208 @@ class Jacket < Thor
270
274
  end # def check()
271
275
 
272
276
 
277
+ #####################################
278
+ # Get stats
279
+ desc 'stats', 'Get stats for each entry in csv format'
280
+ method_option :header, {
281
+ type: :boolean,
282
+ desc: 'Include a header row',
283
+ default: true
284
+ }
285
+ method_option :title, {
286
+ type: :boolean,
287
+ desc: 'Include the entry title',
288
+ default: false
289
+ }
290
+ method_option :time, {
291
+ type: :boolean,
292
+ desc: 'Include the entry date and time',
293
+ default: false
294
+ }
295
+ method_option :output, {
296
+ type: :string,
297
+ desc: 'File name for CSV, if not provided, STDOUT is used'
298
+ }
299
+ def stats()
300
+ # open jacket
301
+ jck = _open_jacket
302
+ return if !jck
303
+
304
+ do_time = options[:time]
305
+ do_title = options[:title]
306
+
307
+ # CSV setup
308
+ if options[:output]
309
+ csv = ::CSV.open(options[:output], 'w', :encoding => 'utf-8')
310
+ else
311
+ csv = ::CSV.new(STDOUT, :encoding => 'utf-8')
312
+ end
313
+
314
+ # do the header row
315
+ if options[:header]
316
+ ha = [
317
+ 'Stat name',
318
+ 'Value',
319
+ 'Account',
320
+ 'Entry',
321
+ 'Revision'
322
+ ]
323
+ ha.push 'Date Time' if do_time
324
+ ha.push 'Title' if do_title
325
+ csv << ha
326
+ end
327
+
328
+ # read each entry and provide stats
329
+ hst = jck.read_history
330
+ hst.entry_max.times do |ix|
331
+ enum = ix + 1
332
+ ent = jck.read_entry(enum)
333
+ stats = ent.stats
334
+ next if stats.empty?
335
+
336
+ rnum = ent.revision
337
+ time = ent.time_str if do_time
338
+ title = ent.title if do_title
339
+
340
+ stats.each do |stat|
341
+ typ, val, accts = stat
342
+ if accts.empty?
343
+ ln = [typ, val, nil, enum, rnum]
344
+ ln.push time if do_time
345
+ ln.push title if do_title
346
+ csv << ln
347
+ next
348
+ end
349
+ accts.each do |acct|
350
+ ln = [typ, val, acct, enum, rnum]
351
+ ln.push time if do_time
352
+ ln.push title if do_title
353
+ csv << ln
354
+ end
355
+ end
356
+ end
357
+
358
+ jck.close
359
+ csv.close
360
+
361
+ end # def stats()
362
+
363
+
364
+ #####################################
365
+ # Backup to aws
366
+ desc 'backup_s3', 'Backup to AWS S3'
367
+ method_option :key, {
368
+ type: :string,
369
+ desc: 'Path of AWS credentials',
370
+ required: true,
371
+ }
372
+ method_option :bucket, {
373
+ type: :string,
374
+ desc: 'S3 bucket name',
375
+ required: true,
376
+ }
377
+ method_option :level, {
378
+ type: :string,
379
+ desc: 'Debug level, "debug", "info", "warn", "error"',
380
+ default: 'error',
381
+ }
382
+ def backup_s3()
383
+ # open jacket
384
+ jck = _open_jacket
385
+ return if !jck
386
+
387
+ # read in JSON config
388
+ json = File.read(options[:key])
389
+ creds = JSON.parse(json)
390
+ opts = {
391
+ region: creds['aws_region'],
392
+ access_key_id: creds['aws_id'],
393
+ secret_access_key: creds['aws_key'],
394
+ }
395
+ log = Logger.new(STDOUT)
396
+ case options[:level]
397
+ when 'debug'
398
+ log.level = Logger::DEBUG
399
+ when 'info'
400
+ log.level = Logger::INFO
401
+ when 'warn'
402
+ log.level = Logger::WARN
403
+ else
404
+ log.level = Logger::ERROR
405
+ end
406
+
407
+ puts 'id: %s' % creds['aws_id']
408
+ puts 'secret: %s' % creds['aws_key']
409
+ puts 'bucket: %s' % options[:bucket]
410
+
411
+ # client
412
+ s3 = ::Aws::S3::Client.new(opts)
413
+ sto = ::Sgfa::StoreS3.new
414
+ sto.open(s3, options[:bucket])
415
+
416
+ # backup
417
+ jck.backup(sto, log: log)
418
+ jck.close
419
+
420
+ end # def backup_s3()
421
+
422
+
423
+ #####################################
424
+ # Restore from AWS S3
425
+ desc 'restore_s3', 'Backup to AWS S3'
426
+ method_option :key, {
427
+ type: :string,
428
+ desc: 'Path of AWS credentials',
429
+ required: true,
430
+ }
431
+ method_option :bucket, {
432
+ type: :string,
433
+ desc: 'S3 bucket name',
434
+ required: true,
435
+ }
436
+ method_option :level, {
437
+ type: :string,
438
+ desc: 'Debug level, "debug", "info", "warn", "error"',
439
+ default: 'error',
440
+ }
441
+ def restore_s3()
442
+ # open jacket
443
+ jck = _open_jacket
444
+ return if !jck
445
+
446
+ # read in JSON config
447
+ json = File.read(options[:key])
448
+ creds = JSON.parse(json)
449
+ opts = {
450
+ region: creds['aws_region'],
451
+ access_key_id: creds['aws_id'],
452
+ secret_access_key: creds['aws_key'],
453
+ }
454
+ log = Logger.new(STDOUT)
455
+ case options[:level]
456
+ when 'debug'
457
+ log.level = Logger::DEBUG
458
+ when 'info'
459
+ log.level = Logger::INFO
460
+ when 'warn'
461
+ log.level = Logger::WARN
462
+ else
463
+ log.level = Logger::ERROR
464
+ end
465
+
466
+ puts 'id: %s' % creds['aws_id']
467
+ puts 'secret: %s' % creds['aws_key']
468
+ puts 'bucket: %s' % options[:bucket]
469
+
470
+ # client
471
+ s3 = ::Aws::S3::Client.new(opts)
472
+ sto = ::Sgfa::StoreS3.new
473
+ sto.open(s3, options[:bucket])
474
+
475
+ # backup
476
+ jck.restore(sto, log: log)
477
+ jck.close
478
+ end # def restore_s3
273
479
 
274
480
  private
275
481