blackstack-deployer 1.2.2

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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/blackstack-deployer.rb +616 -0
  3. metadata +87 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 963efdf1e53a2270ab7e7c1708b402481874ba05fe54a32efc4effedefdc08eb
4
+ data.tar.gz: d36e372a881be2f6e89517c4d194bf35f7f84d38319e0c413010e32903f146de
5
+ SHA512:
6
+ metadata.gz: 930142455038b2a8a5a4a367002c46fad30aecd52b3c41ce15c47da599c009fcb0bd7cfba9efa5985efaae805b9d50f662009930a4740fac99da2f66c5bdb0e7
7
+ data.tar.gz: 3177ece62e6ed4c45909c9bf25e357998c2d97f30f91a5820806849a8f3f134a38a522eb46a992fb47775c1f4c756bc8404096b7c5ca803e900fa2482ac35fd6
@@ -0,0 +1,616 @@
1
+ require 'blackstack-nodes'
2
+ require 'sequel'
3
+
4
+ module BlackStack
5
+ # Deployer is a library that can be used to deploy a cluster of nodes.
6
+ module Deployer
7
+ @@logger = BlackStack::BaseLogger.new(nil)
8
+ @@nodes = []
9
+ @@routines = []
10
+
11
+ # get the logger assigned to the module
12
+ def self.logger
13
+ @@logger
14
+ end # def self.errors
15
+
16
+ # get the array of nodes assigned to the module
17
+ def self.nodes
18
+ @@nodes
19
+ end # def self.nodes
20
+
21
+ # get the array of routines assigned to the module
22
+ def self.routines
23
+ @@routines
24
+ end # def self.routines
25
+
26
+ # inherit BlackStack::Infrastructure::NodeModule, including features of deployer.
27
+ module NodeModule
28
+ attr_accessor :deployment_routine, :parameters
29
+
30
+ include BlackStack::Infrastructure::NodeModule
31
+
32
+ # get the IP address for an interface using the ip addr command.
33
+ # this is a helper method for installing cockroachdb nodes.
34
+ def eth0_ip(interface='eth0')
35
+ a = self.ssh.exec!('ip addr show dev eth0').scan(/inet [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/)
36
+ return nil if a.size == 0
37
+ return a.last.to_s.gsub(/inet /, '')
38
+ end
39
+
40
+ def self.descriptor_errors(h)
41
+ errors = BlackStack::Infrastructure::NodeModule.descriptor_errors(h)
42
+
43
+ # validate: does not exist any other element in @@nodes with the same value for the parameter h[:name]
44
+ errors << "The parameter h[:name] is not unique" if BlackStack::Deployer.nodes.select{|n| n.name == h[:name]}.length > 0
45
+
46
+ # validate: h[:deployment_routine] is not nil
47
+ errors << "The parameter h[:deployment_routine] is required" if h[:deployment_routine].nil?
48
+
49
+ # validate: h[:deployment_routine] is a string
50
+ errors << "The parameter h[:deployment_routine] is not a string" unless h[:deployment_routine].is_a?(String)
51
+
52
+ # return list of errors
53
+ errors.uniq
54
+ end
55
+
56
+ def initialize(h, i_logger=nil)
57
+ self.parameters = h
58
+ errors = BlackStack::Deployer::NodeModule.descriptor_errors(h)
59
+ raise "The node descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
60
+ super(h, i_logger)
61
+ self.deployment_routine = h[:deployment_routine]
62
+ end # def self.create(h)
63
+
64
+ def to_hash
65
+ h = super
66
+ h[:deployment_routine] = self.deployment_routine
67
+ h
68
+ end # def to_hash
69
+ end # module NodeModule
70
+
71
+ # define attributes and methods of a deployer routine
72
+ module RoutineModule
73
+ attr_accessor :name, :commands
74
+
75
+ def self.descriptor_errors(h)
76
+ errors = []
77
+
78
+ # validate: the parameter h is a hash
79
+ errors << "The parameter h is not a hash" unless h.is_a?(Hash)
80
+
81
+ # validate: the paramerer h has a key :name
82
+ errors << "The parameter h does not have a key :name" unless h.has_key?(:name)
83
+
84
+ # validate: the paramerer h has a key :command
85
+ errors << "The parameter h does not have a key :commands" unless h.has_key?(:commands)
86
+
87
+ # validate: the parameter h[:name] is a string or a symbol
88
+ errors << "The parameter h[:name] is not a string" unless h[:name].is_a?(String)
89
+
90
+ # validate: the parameter h[:name] is not 'reboot' because it is a reserved name
91
+ errors << "The parameter h[:name] is a reserved name (#{h[:name].to_s})" if h[:name] == 'reboot'
92
+
93
+ # validate: the parameter h[:commands] is required
94
+ errors << "The parameter h[:commands] is required" if h[:commands].nil?
95
+
96
+ # validate: the parametrer h[:commands] is an array
97
+ errors << "The parameter h[:commands] is not an array" unless h[:commands].is_a?(Array)
98
+
99
+ # validate: the parameter h[:commands] has at least one element
100
+ errors << "The parameter h[:commands] does not have at least one element" unless h[:commands].size > 0
101
+
102
+ # validate: each element of the array h[:commands] is a hash
103
+ h[:commands].each do |c|
104
+ errors += BlackStack::Deployer::CommandModule.descriptor_errors(c)
105
+ end # h[:commands].each do |c|
106
+
107
+ errors.uniq
108
+ end # def self.descriptor_error(h)
109
+
110
+ def initialize(h)
111
+ errors = BlackStack::Deployer::RoutineModule.descriptor_errors(h)
112
+ raise "The node descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
113
+ self.name = h[:name]
114
+ self.commands = []
115
+ h[:commands].each do |c|
116
+ self.commands << BlackStack::Deployer::Command.new(c)
117
+ end
118
+ end
119
+
120
+ def to_hash
121
+ h = {}
122
+ h[:name] = self.name
123
+ h[:commands] = []
124
+ self.commands.each do |c|
125
+ h[:commands] << c.to_hash
126
+ end
127
+ h
128
+ end
129
+
130
+ def run(node)
131
+ ret = []
132
+ self.commands.each do |c|
133
+ BlackStack::Deployer.logger.logs "Running command: #{c.command.to_s}... "
134
+ h = c.run(node)
135
+ ret << h
136
+
137
+ BlackStack::Deployer.logger.logs "Result: "
138
+ BlackStack::Deployer.logger.logf h.to_s
139
+
140
+ if h[:errors].size == 0
141
+ BlackStack::Deployer.logger.done
142
+ else
143
+ BlackStack::Deployer.logger.logf('error: ' + h.to_s)
144
+ raise "Error running command: #{h.to_s}"
145
+ end
146
+ end
147
+ ret
148
+ end # def run(node)
149
+ end # module RoutineModule
150
+
151
+ # define attributes and methods of a routine's command
152
+ module CommandModule
153
+ attr_accessor :command, :matches, :nomatches, :sudo
154
+
155
+ def self.descriptor_errors(c)
156
+ errors = []
157
+
158
+ # validate: h is a hash
159
+ errors << "The command descriptor is not a hash" unless c.is_a?(Hash)
160
+
161
+ # validate: the hash c has a key :command
162
+ errors << "The command descriptor does not have a key :command" unless c.has_key?(:command)
163
+
164
+ # validate: the value of c[:command] is a string or symbol
165
+ errors << "The value of c[:command] is not a string and is not a symbol" unless c[:command].is_a?(String) || c[:command].is_a?(Symbol)
166
+
167
+ # validate: if the key :sudo exists, then its value is a boolean
168
+ if c.has_key?(:sudo)
169
+ errors << "The value of c[:sudo] is not a boolean" unless c[:sudo].is_a?(TrueClass) || c[:sudo].is_a?(FalseClass)
170
+ end
171
+
172
+ # if the parameter h[:name] is a symbol
173
+ if c[:command].is_a?(Symbol)
174
+ if c[:command] == :reboot
175
+ # :reboot is a reserved word, so it is fine to call :reboot
176
+ else
177
+ # validate: existis a routine with a the value c[:command].to_s on its :name key
178
+ errors << "The routine with the name #{c[:command].to_s} does not exist" unless BlackStack::Deployer::routines.select { |r| r.name == c[:command].to_s }.size > 0
179
+ end
180
+ end
181
+
182
+ # if c[:matches] exists
183
+ if c.has_key?(:matches)
184
+ # validate: the value of c[:matches] must by a regex or an array
185
+ errors << "The value of the key :matches is not a regex nor an array" unless c[:matches].is_a?(Regexp) || c[:matches].is_a?(Array)
186
+ # if c[:matches] is a array
187
+ if c[:matches].is_a?(Array)
188
+ # validate: each element in the the array c[:matches] is a regex
189
+ c[:matches].each do |m|
190
+ errors += BlackStack::Deployer::MatchModule.descriptor_errors(m)
191
+ end # each
192
+ end # if c[:matches].is_a?(Array)
193
+ end # if :matches exists
194
+
195
+ # if c[:nomatches] exists
196
+ if c.has_key?(:nomatches)
197
+ # validate: the value of c[:nomatches] must by a regex or an array
198
+ errors << "The value of the key :nomatches is not a regex nor an array" unless c[:nomatches].is_a?(Regexp) || c[:nomatches].is_a?(Array)
199
+ # if c[:nomatches] is a array
200
+ if c[:nomatches].is_a?(Array)
201
+ # validate: each element in the the array c[:nomatches] is a hash
202
+ c[:nomatches].each do |m|
203
+ errors += BlackStack::Deployer::NoMatchModule.descriptor_errors(m)
204
+ end # each
205
+ end # if c[:matches].is_a?(Array)
206
+ end # if :matches exists
207
+ #
208
+ errors.uniq
209
+ end # def self.descriptor_error(h)
210
+
211
+ def initialize(h)
212
+ errors = BlackStack::Deployer::CommandModule.descriptor_errors(h)
213
+ raise "The node descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
214
+ self.command = h[:command]
215
+ self.sudo = h[:sudo].nil? ? true : h[:sudo]
216
+ self.matches = []
217
+ self.nomatches = []
218
+ if h.has_key?(:matches)
219
+ if h[:matches].is_a?(Regexp)
220
+ self.matches << BlackStack::Deployer::Match.new(h[:matches])
221
+ else
222
+ h[:matches].each do |m|
223
+ self.matches << BlackStack::Deployer::Match.new(m)
224
+ end
225
+ end
226
+ end
227
+ if h.has_key?(:nomatches)
228
+ if h[:nomatches].is_a?(Regexp)
229
+ self.nomatches << BlackStack::Deployer::NoMatch.new(h[:nomatches])
230
+ else
231
+ h[:nomatches].each do |m|
232
+ self.nomatches << BlackStack::Deployer::NoMatch.new(m)
233
+ end
234
+ end
235
+ end
236
+ end # def initialize(h)
237
+
238
+ def to_hash
239
+ h = {}
240
+ h[:command] = self.command
241
+ h[:sudo] = self.sudo
242
+ h[:matches] = []
243
+ h[:nomatches] = []
244
+ self.matches.each do |m|
245
+ h[:matches] << m.to_hash
246
+ end
247
+ self.nomatches.each do |m|
248
+ h[:nomatches] << m.to_hash
249
+ end
250
+ h
251
+ end # def to_hash
252
+
253
+ def run(node)
254
+ errors = []
255
+ code = self.command
256
+ output = nil
257
+
258
+ # if code is a symbol
259
+ if code.is_a?(Symbol)
260
+
261
+ # if code is equel than :reboot
262
+ if code == :reboot
263
+ # call the node reboot method
264
+ node.reboot
265
+ else
266
+ # look for a routine with this name
267
+ r = BlackStack::Deployer.routines.select { |r| r.name == code.to_s }.first
268
+ if !r.nil?
269
+ r.run(node)
270
+ else
271
+ raise "The routine #{code.to_s} does not exist"
272
+ end
273
+ end
274
+
275
+ # if code is a string
276
+ elsif code.is_a?(String)
277
+ # replacing parameters
278
+ code.scan(/%[a-zA-Z0-9\_]+%/).each do |p|
279
+ if p == '%eth0_ip%' # reserved parameter
280
+ # TODO: move the method eth0_ip to the blackstack-nodes library
281
+ code.gsub!(p, node.eth0_ip)
282
+ elsif p == '%timestamp%' # reserved parameter
283
+ # TODO: move this to a timestamp function on blackstack-core
284
+ code.gsub!(p, Time.now.to_s.gsub(/\D/, ''))
285
+ else
286
+ if node.parameters.has_key?(p.gsub(/%/, '').to_sym)
287
+ code.gsub!(p, node.parameters[p.gsub(/%/, '').to_sym].to_s)
288
+ else
289
+ raise "The parameter #{p} does not exist in the node descriptor #{node.parameters.to_s}"
290
+ end
291
+ end
292
+ end
293
+
294
+ # running the command
295
+ if self.sudo
296
+ # escale the single quotes in the code variable
297
+ code.gsub!(/'/, "\\\\'")
298
+
299
+ if node.ssh_private_key_file.nil?
300
+ code = "echo '#{node.ssh_password.to_s.gsub("'", "\\\\'")}' | sudo -S su root -c '#{code.to_s}'"
301
+ else
302
+ code = "sudo -S su root -c '#{code.to_s}'"
303
+ end
304
+ else
305
+ code = code.to_s
306
+ end
307
+ #puts
308
+ #puts code
309
+ #exit(0)
310
+ output = node.ssh.exec!(code)
311
+
312
+ # validation: at least one of the matches should happen
313
+ if self.matches.size > 0
314
+ i = 0
315
+ self.matches.each do |m|
316
+ if m.validate(output).size == 0 # no errors
317
+ i += 1
318
+ end
319
+ end
320
+ errors << "Command output doesn't match with any of the :matches" if i == 0
321
+ end # if self.matches.size > 0
322
+
323
+ # validation: no one of the nomatches should happen
324
+ self.nomatches.each do |m|
325
+ errors += m.validate(output)
326
+ end
327
+ end # elsif code.is_a?(String)
328
+
329
+ # return a hash descriptor of the command result
330
+ {
331
+ :command => self.command,
332
+ :code => code,
333
+ :output => output,
334
+ :errors => errors,
335
+ }
336
+ end # def run(node)
337
+ end # module CommandModule
338
+
339
+ # define attributes and methods of a command's match
340
+ module MatchModule
341
+ attr_accessor :match
342
+
343
+ def self.descriptor_errors(m)
344
+ errors = []
345
+ # validate the match is a regular expresion
346
+ errors << "The match is not a regex" unless m.is_a?(Regexp)
347
+ #
348
+ errors.uniq
349
+ end # def self.descriptor_error(h)
350
+
351
+ def initialize(h)
352
+ errors = BlackStack::Deployer::MatchModule.descriptor_errors(h)
353
+ raise "The node descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
354
+ self.match = h
355
+ end
356
+
357
+ def to_hash
358
+ h = {}
359
+ h[:match] = self.match
360
+ h
361
+ end
362
+
363
+ def validate(output)
364
+ errors = []
365
+ errors << "The output of the command does not match the regular expression #{self.match.inspect}" unless output.match(self.match)
366
+ errors
367
+ end
368
+ end # module MatchModule
369
+
370
+ # define attributes and methods of a command's nomatch
371
+ module NoMatchModule
372
+ attr_accessor :nomatch, :error_description
373
+
374
+ def self.descriptor_errors(m)
375
+ errors = []
376
+ # validate the nomatch is a regular expresion
377
+ errors << "Each nomatch is not a regex nor a hash" unless m.is_a?(Hash) || m.is_a?(Regexp)
378
+ # if m is a hash
379
+ if m.is_a?(Hash)
380
+ # validate: the hash m has a key :nomatch
381
+ errors << "The hash descriptor of the nomatch does not have a key :nomatch" unless m.has_key?(:nomatch)
382
+ # validate: the value of m[:nomatch] is a string
383
+ errors << "The value of the key :nomatch is not a regex" unless m[:nomatch].is_a?(Regexp)
384
+ # validate: the hash m has a key :error_description
385
+ errors << "The hash descriptor of the nomatch does not have a key :error_description" unless m.has_key?(:error_description)
386
+ # validate: the value of m[:error_description] is a string
387
+ errors << "The value of the key :error_description is not a string" unless m[:error_description].is_a?(String)
388
+ end # if m.is_a?(Hash)
389
+ #
390
+ errors.uniq
391
+ end # def self.descriptor_error(h)
392
+
393
+ def initialize(h)
394
+ errors = BlackStack::Deployer::NoMatchModule.descriptor_errors(h)
395
+ raise "The node descriptor is not valid: #{errors.uniq.join(".\n")}" if errors.length > 0
396
+ self.nomatch = h[:nomatch]
397
+ self.error_description = h[:error_description]
398
+ end
399
+
400
+ def to_hash
401
+ h = {}
402
+ h[:nomatch] = self.nomatch
403
+ h[:error_description] = self.error_description
404
+ h
405
+ end
406
+
407
+ def validate(output)
408
+ errors = []
409
+ if !self.error_description.nil?
410
+ errors << self.error_description if output.match(self.nomatch)
411
+ else
412
+ errors << "The output of the command matches the regular expression #{self.nomatch.inspect}" if output.match(self.nomatch)
413
+ end
414
+ errors
415
+ end
416
+
417
+ end # module NoMatchModule
418
+
419
+
420
+ # TODO: declare these classes (stub and skeleton) using blackstack-rpc
421
+ #
422
+ # Stub Classes
423
+ # These classes represents a node, without using connection to the database.
424
+ # Use this class at the client side.
425
+ class Node
426
+ include BlackStack::Deployer::NodeModule
427
+ end # class Node
428
+
429
+ class Command
430
+ include BlackStack::Deployer::CommandModule
431
+ end # class Command
432
+
433
+ class Routine
434
+ include BlackStack::Deployer::RoutineModule
435
+ end # class Routine
436
+
437
+ class Match
438
+ include BlackStack::Deployer::MatchModule
439
+ end # class Match
440
+
441
+ class NoMatch
442
+ include BlackStack::Deployer::NoMatchModule
443
+ end # class NoMatch
444
+
445
+ # add a node to the list of nodes.
446
+ def self.add_node(h)
447
+ errors = BlackStack::Deployer::NodeModule.descriptor_errors(h)
448
+ raise errors.join(".\n") unless errors.empty?
449
+ @@nodes << BlackStack::Deployer::Node.new(h, @@logger)
450
+ end # def
451
+
452
+ # add an array of nodes to the list of nodes.
453
+ def self.add_nodes(a)
454
+ # validate: the parameter a is an array
455
+ raise "The parameter a is not an array" unless a.is_a?(Array)
456
+ a.each { |h| BlackStack::Deployer.add_node(h) }
457
+ end # def
458
+
459
+ # remove all exisiting nodes in he list of nodes.
460
+ # then, add the nodes in the parameter a to the list of nodes.
461
+ def self.set_nodes(a)
462
+ @@nodes.clear
463
+ BlackStack::Deployer.add_nodes(a)
464
+ end # def
465
+
466
+ # add a routine to the list of routines.
467
+ def self.add_routine(h)
468
+ errors = BlackStack::Deployer::RoutineModule.descriptor_errors(h)
469
+ raise errors.join(".\n") unless errors.empty?
470
+ @@routines << BlackStack::Deployer::Routine.new(h)
471
+ end # def
472
+
473
+ # add an array of routines to the list of routines.
474
+ def self.add_routines(a)
475
+ # validate: the parameter a is an array
476
+ raise "The parameter a is not an array" unless a.is_a?(Array)
477
+ a.each { |h| BlackStack::Deployer.add_routine(h) }
478
+ end # def
479
+
480
+ # remove all exisiting routines in he list of routines.
481
+ # then, add the routines in the parameter a to the list of routines.
482
+ def self.set_routines(a)
483
+ @@routines.clear
484
+ BlackStack::Deployer.add_routines(a)
485
+ end # def
486
+
487
+ # running a routine on a node
488
+ def self.run_routine(node_name, routine_name)
489
+ errors = []
490
+
491
+ # find the node with the value node_name in the key :name
492
+ n = @@nodes.select { |n| n.name == node_name }.first
493
+
494
+ # find the routine with the value routine_name in the key :name
495
+ r = @@routines.select { |r| r.name == routine_name }.first
496
+
497
+ # validate: the node n exists
498
+ errors << "Node #{node_name} not found" unless n
499
+
500
+ # validate: the routine r exists
501
+ errors << "Routine #{routine_name} not found" unless r
502
+
503
+ # raise exception if any error has been found
504
+ raise "The routine #{routine_name} cannot be run on the node #{node_name}: #{errors.uniq.join(".\n")}" if errors.length > 0
505
+
506
+ # connect the node
507
+ self.logger.logs "Connecting to node #{n.name}... "
508
+ n.connect
509
+ self.logger.done
510
+
511
+ # run the routine
512
+ self.logger.logs "Running routine #{r.name}... "
513
+ r.run(n)
514
+ self.logger.done
515
+
516
+ # disconnect the node
517
+ self.logger.logs "Disconnecting from node #{n.name}... "
518
+ n.disconnect
519
+ self.logger.done
520
+
521
+ end # def
522
+
523
+ module DB
524
+ @@checkpoint = nil
525
+ @@superhuser = nil
526
+ @@ndb = nil
527
+ @@folder = nil
528
+
529
+ def self.set_checkpoint(s)
530
+ @@checkpoint = s
531
+ end
532
+
533
+ def self.connect(s)
534
+ @@db = Sequel::connect(s)
535
+ end # def
536
+
537
+ def self.set_folder(s)
538
+ @@folder = s
539
+ end # def
540
+
541
+
542
+ # Return `true` if the name of the file matches with `/\.transactions\./`, and it doesn't match with `/\.sentences\./`, and the matches with `/\.transactions\./` are no more than one.
543
+ # Otherwise, return `false`.
544
+ # This method should not be called directly by user code.
545
+ def self.is_transactions_file?(filename)
546
+ filename =~ /\.transactions\./ && filename !~ /\.sentences\./ && filename.scan(/\.transactions\./).size == 1
547
+ end
548
+
549
+ # Return `true` if the name of the file matches with `/\.sentences\./`, and it doesn't match with `/\.transactions\./`, and the matches with `/\.sentences\./` are no more than one.
550
+ # Otherwise({, return `false`.
551
+ # This method should not be called directly by user code.
552
+ def self.is_sentences_file?(filename)
553
+ filename =~ /\.sentences\./ && filename !~ /\.transactions\./ && filename.scan(/\.sentences\./).size == 1
554
+ end
555
+
556
+ # Method to process an `.sql` file with transactions code, separated by `BEGIN;` and `COMMIT;` statements.
557
+ # Reference: https://stackoverflow.com/questions/64066344/import-large-sql-files-with-ruby-sequel-gem
558
+ # This method is called by `BlackStack::Deployer::db_execute_file` if the filename matches with `/\.tsql\./`.
559
+ # This method should not be called directly by user code.
560
+ def self.execute_transactions(sql)
561
+ # TODO: Code Me!
562
+ end # def db_execute_tsql
563
+
564
+ # Method to process an `.sql` file with one sql sentence by line.
565
+ # Reference: https://stackoverflow.com/questions/64066344/import-large-sql-files-with-ruby-sequel-gem
566
+ # This method is called by `BlackStack::Deployer::db_execute_file` if the filename matches with `/\.sentences\./`.
567
+ # This method should not be called directly by user code.
568
+ def self.execute_sentences(sql)
569
+ tlogger = BlackStack::Deployer::logger
570
+
571
+ # Fix issue: Ruby `split': invalid byte sequence in UTF-8 (ArgumentError)
572
+ # Reference: https://stackoverflow.com/questions/11065962/ruby-split-invalid-byte-sequence-in-utf-8-argumenterror
573
+ sql.encode!('UTF-8', :invalid => :replace)
574
+
575
+ # Keeping only ASCII characters
576
+ # Ruby: https://programming-idioms.org/idiom/147/remove-all-non-ascii-characters/1903/ruby
577
+ sql.split(/;/i).each { |statement|
578
+ statement = statement.to_s.strip
579
+ tlogger.logs "#{statement.split("\n").first}... "
580
+ begin
581
+ @@db.execute(statement) #if statement.to_s.strip.size > 0
582
+ tlogger.done
583
+ rescue => e
584
+ tlogger.logf e.to_s
585
+ raise "Error executing statement: #{statement}\n#{e.message}"
586
+ end
587
+ }
588
+ tlogger.done
589
+ end # def db_execute_sql_sentences_file
590
+
591
+ # Run a series of `.sql` files with updates to the database.
592
+ #
593
+ def self.deploy()
594
+ tlogger = BlackStack::Deployer::logger
595
+ # get list of `.sql` files in the directory `sql_path`, with a name higher than `last_filename`, sorted by name.
596
+ Dir.entries(@@folder).select {
597
+ |f| f =~ /\.sql$/ && f > @@checkpoint.to_s
598
+ }.uniq.sort.each { |filename|
599
+ fullfilename = "#{@@folder}/#{filename}"
600
+ #puts fullfilename
601
+ #puts File.open(fullfilename).read
602
+ tlogger.logs "#{fullfilename}... "
603
+ BlackStack::Deployer::DB::execute_sentences( File.open(fullfilename).read )
604
+ tlogger.done
605
+ }
606
+ end # def
607
+ end # module DB
608
+
609
+ # deploying all db-updates and run all routines on all nodes
610
+ def self.deploy()
611
+ # TODO: Code Me!
612
+ end # def
613
+
614
+ end # module Deployer
615
+
616
+ end # module BlackStack
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blackstack-deployer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Leandro Daniel Sardi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-06-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: blackstack-nodes
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.2
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.2
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.2.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: sequel
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 5.56.0
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 5.56.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 5.56.0
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 5.56.0
53
+ description: "BlackStack Deployer automates what you already know how to do manually,
54
+ but in a repeatable, scalable fashion. There is no magic here!\nBlackStack Deployer
55
+ dutifully connects to your server(s) via SSH and executes the steps necessary to
56
+ deploy your project. \nYou can define those steps yourself, or by using pre-built
57
+ task libraries provided by the BlackStack Deployer community.\n "
58
+ email: leandro.sardi@expandedventure.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - lib/blackstack-deployer.rb
64
+ homepage: https://rubygems.org/gems/blackstack-deployer
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubygems_version: 3.3.7
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: BlackStack Deployer is a deployment automation tool built on Ruby and SSH.
87
+ test_files: []