bluepill 0.1.2 → 0.1.4

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 (43) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +820 -0
  3. data/CONTRIBUTING.md +31 -0
  4. data/LICENSE +1 -1
  5. data/README.md +69 -30
  6. data/bin/bluepill +2 -2
  7. data/bluepill.gemspec +17 -6
  8. data/lib/bluepill/application.rb +311 -43
  9. data/lib/bluepill/condition_watch.rb +105 -10
  10. data/lib/bluepill/controller.rb +125 -14
  11. data/lib/bluepill/dsl/app_proxy.rb +103 -4
  12. data/lib/bluepill/dsl/process_factory.rb +185 -62
  13. data/lib/bluepill/dsl/process_proxy.rb +132 -14
  14. data/lib/bluepill/dsl.rb +27 -5
  15. data/lib/bluepill/group.rb +142 -9
  16. data/lib/bluepill/logger.rb +122 -7
  17. data/lib/bluepill/process.rb +582 -211
  18. data/lib/bluepill/process_commands.rb +262 -0
  19. data/lib/bluepill/process_conditions/always_true.rb +18 -4
  20. data/lib/bluepill/process_conditions/cpu_usage.rb +34 -0
  21. data/lib/bluepill/process_conditions/file_time.rb +40 -4
  22. data/lib/bluepill/process_conditions/http.rb +81 -23
  23. data/lib/bluepill/process_conditions/mem_usage.rb +64 -5
  24. data/lib/bluepill/process_conditions/process_condition.rb +45 -2
  25. data/lib/bluepill/process_conditions/running_time.rb +36 -1
  26. data/lib/bluepill/process_conditions/zombie_process.rb +26 -5
  27. data/lib/bluepill/process_conditions.rb +15 -1
  28. data/lib/bluepill/process_journal.rb +256 -104
  29. data/lib/bluepill/process_statistics.rb +33 -3
  30. data/lib/bluepill/socket.rb +63 -16
  31. data/lib/bluepill/system/commands.rb +286 -0
  32. data/lib/bluepill/system.rb +193 -229
  33. data/lib/bluepill/trigger.rb +111 -15
  34. data/lib/bluepill/triggers/flapping.rb +86 -5
  35. data/lib/bluepill/util/rotational_array.rb +25 -2
  36. data/lib/bluepill/version.rb +71 -19
  37. data/lib/bluepill.rb +25 -2
  38. data/sig/bluepill.rbs +926 -0
  39. data/sig/manifest.yaml +7 -0
  40. metadata +16 -35
  41. data/bin/sample_forking_server +0 -66
  42. data/lib/bluepill/application/client.rb +0 -6
  43. data/lib/bluepill/application/server.rb +0 -21
data/sig/bluepill.rbs ADDED
@@ -0,0 +1,926 @@
1
+ # Type signatures for bluepill
2
+
3
+ module Bluepill
4
+ # The value of Bluepill::System.ps_axu: ps fields by pid
5
+ type ps_table = Hash[Integer, Array[String]]
6
+
7
+ # A time in seconds. An ActiveSupport::Duration also works, since it
8
+ # passes for its value.
9
+ type seconds = Integer | Float
10
+
11
+ # A process block from the config, which runs with self set to the
12
+ # ProcessProxy and also gets it as an argument
13
+ type process_block = ^(ProcessProxy process) [self: ProcessProxy] -> void
14
+
15
+ # Sets up Bundler when bluepill runs with a Gemfile, such as under bundle
16
+ # exec
17
+ def self.setup_bundler: (?String? gemfile) -> void
18
+
19
+ # Loads an application from a config file. The block runs with self set to
20
+ # the AppProxy, and also gets it as an argument.
21
+ def self.application: (_ToS app_name, ?Hash[Symbol, untyped] options) { (AppProxy app) [self: AppProxy] -> void } -> void
22
+
23
+ module Version
24
+ def self.major: () -> Integer
25
+
26
+ def self.minor: () -> Integer
27
+
28
+ def self.patch: () -> Integer
29
+
30
+ def self.pre: () -> Integer?
31
+
32
+ def self.to_h: () -> { major: Integer, minor: Integer, patch: Integer, pre: Integer? }
33
+
34
+ def self.to_a: () -> Array[Integer]
35
+
36
+ def self.to_s: () -> String
37
+ end
38
+
39
+ # The daemon: it holds the groups of processes and serves commands from
40
+ # bluepill's socket
41
+ class Application
42
+ PROCESS_COMMANDS: Array[Symbol]
43
+
44
+ attr_accessor name: String
45
+ attr_accessor logger: Logger
46
+ attr_accessor base_dir: String
47
+ attr_accessor socket: UNIXServer?
48
+ attr_accessor pid_file: String
49
+ attr_accessor kill_timeout: Integer
50
+ attr_accessor groups: Hash[String?, Group]
51
+ # Not used by bluepill
52
+ attr_accessor work_queue: untyped
53
+ attr_accessor pids_dir: String
54
+ attr_accessor log_file: String?
55
+
56
+ @foreground: bool?
57
+ @mutex: Thread::Mutex
58
+ @listener_thread: Thread?
59
+ @running: bool
60
+
61
+ def initialize: (String name, ?Hash[Symbol, untyped] options) -> void
62
+
63
+ def foreground?: () -> bool
64
+
65
+ def mutex: [T] () { () -> T } -> T
66
+
67
+ def load: () -> void
68
+
69
+ # The PROCESS_COMMANDS, which return the affected processes, or the
70
+ # status lines for status
71
+ def start: (?String? group_name, ?String? process_name) -> Array[String]
72
+
73
+ def stop: (?String? group_name, ?String? process_name) -> Array[String]
74
+
75
+ def restart: (?String? group_name, ?String? process_name) -> Array[String]
76
+
77
+ def unmonitor: (?String? group_name, ?String? process_name) -> Array[String]
78
+
79
+ def status: (?String? group_name, ?String? process_name) -> Array[String]
80
+
81
+ def add_process: (Process process, ?_ToS? group_name) -> Array[Process]
82
+
83
+ def version: () -> singleton(Version)
84
+
85
+ # Ruby's protected methods are private in RBS
86
+
87
+ private
88
+
89
+ def send_to_process_or_group: (Symbol method, String? group_name, String? process_name) -> Array[String]
90
+
91
+ def start_listener: () -> Thread
92
+
93
+ def start_server: () -> void
94
+
95
+ def run: () -> void
96
+
97
+ def cleanup: () -> void
98
+
99
+ def setup_signal_traps: () -> void
100
+
101
+ def setup_logger_and_pids_dir: () -> void
102
+
103
+ def setup_pids_dir: () -> void
104
+
105
+ def kill_previous_bluepill: () -> void
106
+
107
+ def write_pid_file: () -> void
108
+ end
109
+
110
+ # Runs the bluepill commands, and sends them to the daemon over its socket
111
+ class Controller
112
+ attr_accessor base_dir: String
113
+ attr_accessor log_file: String?
114
+ attr_accessor sockets_dir: String
115
+ attr_accessor pids_dir: String
116
+
117
+ def initialize: (Hash[Symbol, untyped] options) -> void
118
+
119
+ def running_applications: () -> Array[String]
120
+
121
+ def handle_command: (String application, String | Symbol command, *String args) -> void
122
+
123
+ # Returns what the daemon responds with, which is unmarshaled from the socket
124
+ def send_to_daemon: (String application, String | Symbol command, *String args) -> untyped
125
+
126
+ def grep_pattern: (String application, ?String? query) -> String
127
+
128
+ private
129
+
130
+ def cleanup_bluepill_directory: () -> void
131
+
132
+ def pid_for: (String app) -> (Integer | false)
133
+
134
+ def setup_dir_structure: () -> void
135
+
136
+ def verify_version!: (String application) -> void
137
+ end
138
+
139
+ module Socket
140
+ TIMEOUT: Integer
141
+ MAX_ATTEMPTS: Integer
142
+
143
+ def self.client: [T] (String base_dir, String name) { (UNIXSocket socket) -> T } -> T
144
+
145
+ # Returns the daemon's response, unmarshaled
146
+ def self.client_command: (String base_dir, String name, String command) -> untyped
147
+
148
+ def self.server: (String base_dir, String name) -> UNIXServer
149
+
150
+ def self.socket_path: (String base_dir, String name) -> String
151
+ end
152
+
153
+ # Runs commands for a Process, which is the only class that includes it
154
+ module ProcessCommands : _Host
155
+ # What ProcessCommands uses from Process
156
+ interface _Host
157
+ %a{pure} def name: () -> String
158
+ %a{pure} def logger: () -> Logger
159
+ %a{pure} def children: () -> Array[Process]
160
+ %a{pure} def pre_start_command: () -> String?
161
+ %a{pure} def start_command: () -> String?
162
+ %a{pure} def stop_command: () -> String?
163
+ %a{pure} def restart_command: () -> String?
164
+ %a{pure} def stop_signals: () -> Array[Symbol | Integer]?
165
+ %a{pure} def start_grace_time: () -> seconds
166
+ %a{pure} def stop_grace_time: () -> seconds
167
+ %a{pure} def restart_grace_time: () -> seconds
168
+ %a{pure} def on_start_timeout: () -> (String | Symbol)
169
+ %a{pure} def monitor_children: () -> bool
170
+ %a{pure} def uid: () -> String?
171
+ %a{pure} def gid: () -> String?
172
+ %a{pure} def working_dir: () -> String?
173
+ %a{pure} def environment: () -> Hash[_ToS, _ToS]?
174
+ %a{pure} def pid_file: () -> String?
175
+ %a{pure} def stdin: () -> String?
176
+ %a{pure} def stdout: () -> String?
177
+ %a{pure} def stderr: () -> String?
178
+ %a{pure} def supplementary_groups: () -> Array[String]?
179
+ def daemonize?: () -> bool
180
+ def monitor_children?: () -> bool
181
+ def actual_pid: () -> Integer?
182
+ def clean_threads: () -> void
183
+ def signal_process: (Integer | String | Symbol code) -> bool
184
+ def unlink_pid: () -> void
185
+ def skip_ticks_for: (seconds seconds) -> Integer
186
+ def dispatch!: (Symbol | String event, ?String? reason) -> bool
187
+ end
188
+
189
+ @threads: Array[Thread]
190
+
191
+ def start_process: () -> Integer
192
+
193
+ def pre_start_process: () -> void
194
+
195
+ def stop_process: () -> Integer
196
+
197
+ def restart_process: () -> Integer
198
+
199
+ def cleanup_process: () -> void
200
+
201
+ def prepare_command: (String command) -> String
202
+
203
+ def system_command_options: () -> System::command_options
204
+
205
+ # Runs the block, and dispatches next_state if it takes longer than secs
206
+ def with_timeout: [T] (seconds secs, String | Symbol next_state) { (Numeric secs) -> T } -> (T | bool)
207
+
208
+ def run_command: (String cmd, String description) -> void
209
+
210
+ def send_stop_signals: (Array[Symbol | Integer] signals) -> void
211
+
212
+ def wait_for_stop: (seconds seconds) -> void
213
+ end
214
+
215
+ # What state_machine passes to transition callbacks and triggers
216
+ interface _Transition
217
+ def from_name: () -> Symbol?
218
+
219
+ def to_name: () -> Symbol?
220
+
221
+ def loopback?: () -> bool
222
+ end
223
+
224
+ # A monitored process
225
+ class Process
226
+ include ProcessCommands
227
+
228
+ CONFIGURABLE_ATTRIBUTES: Array[Symbol]
229
+
230
+ attr_accessor name: String
231
+ attr_accessor watches: Array[ConditionWatch]
232
+ attr_accessor triggers: Array[Trigger]
233
+ attr_accessor skip_ticks_until: Integer?
234
+ attr_accessor process_running: bool?
235
+
236
+ # CONFIGURABLE_ATTRIBUTES
237
+ attr_accessor pre_start_command: String?
238
+ attr_accessor start_command: String?
239
+ attr_accessor stop_command: String?
240
+ attr_accessor restart_command: String?
241
+ attr_accessor stdout: String?
242
+ attr_accessor stderr: String?
243
+ attr_accessor stdin: String?
244
+ attr_accessor daemonize: bool?
245
+ attr_accessor pid_file: String?
246
+ attr_accessor working_dir: String?
247
+ attr_accessor environment: Hash[_ToS, _ToS]?
248
+ attr_accessor start_grace_time: seconds
249
+ attr_accessor stop_grace_time: seconds
250
+ attr_accessor restart_grace_time: seconds
251
+ attr_accessor uid: String?
252
+ attr_accessor gid: String?
253
+ attr_accessor cache_actual_pid: bool
254
+ attr_accessor monitor_children: bool
255
+ attr_accessor child_process_factory: ProcessFactory?
256
+ attr_accessor pid_command: String?
257
+ attr_accessor auto_start: bool?
258
+ attr_accessor supplementary_groups: Array[String]?
259
+ # Signal names alternating with delays in seconds
260
+ attr_accessor stop_signals: Array[Symbol | Integer]?
261
+ attr_accessor on_start_timeout: String | Symbol
262
+ attr_accessor group_start_noblock: bool
263
+ attr_accessor group_restart_noblock: bool
264
+ attr_accessor group_stop_noblock: bool
265
+ attr_accessor group_unmonitor_noblock: bool
266
+
267
+ attr_reader children: Array[Process]
268
+ attr_reader statistics: ProcessStatistics
269
+ attr_reader logger: Logger
270
+
271
+ @event_mutex: Monitor
272
+ @threads: Array[Thread]
273
+ @actual_pid: Integer?
274
+ @transitioned: bool
275
+
276
+ # Methods that state_machine defines, in a module it includes
277
+ interface _StateMachine
278
+ def state: () -> String
279
+
280
+ def state=: (String state) -> String
281
+
282
+ def state_name: () -> Symbol
283
+
284
+ def human_state_name: () -> String
285
+
286
+ def state?: (Symbol state) -> bool
287
+
288
+ def unmonitored?: () -> bool
289
+
290
+ def up?: () -> bool
291
+
292
+ def down?: () -> bool
293
+
294
+ def starting?: () -> bool
295
+
296
+ def stopping?: () -> bool
297
+
298
+ def restarting?: () -> bool
299
+
300
+ def state_events: () -> Array[Symbol]
301
+
302
+ def state_transitions: () -> Array[_Transition]
303
+
304
+ def fire_state_event: (Symbol | String event) -> bool
305
+
306
+ def can_tick?: () -> bool
307
+
308
+ def tick_transition: () -> _Transition?
309
+
310
+ def tick!: () -> true
311
+
312
+ def start: () -> bool
313
+
314
+ def can_start?: () -> bool
315
+
316
+ def start_transition: () -> _Transition?
317
+
318
+ def start!: () -> true
319
+
320
+ def stop: () -> bool
321
+
322
+ def can_stop?: () -> bool
323
+
324
+ def stop_transition: () -> _Transition?
325
+
326
+ def stop!: () -> true
327
+
328
+ def unmonitor: () -> bool
329
+
330
+ def can_unmonitor?: () -> bool
331
+
332
+ def unmonitor_transition: () -> _Transition?
333
+
334
+ def unmonitor!: () -> true
335
+
336
+ def restart: () -> bool
337
+
338
+ def can_restart?: () -> bool
339
+
340
+ def restart_transition: () -> _Transition?
341
+
342
+ def restart!: () -> true
343
+ end
344
+
345
+ include _StateMachine
346
+
347
+ # Methods bluepill defines
348
+
349
+ def initialize: (String process_name, Hash[Symbol, Hash[Symbol, untyped]] checks, ?Hash[Symbol, untyped] options) -> void
350
+
351
+ # Runs the state machine's tick event, then the watches and children
352
+ def tick: () -> void
353
+
354
+ def logger=: (Logger logger) -> void
355
+
356
+ def dispatch!: (Symbol | String event, ?String? reason) -> bool
357
+
358
+ def record_transition: (_Transition transition) -> void
359
+
360
+ def notify_triggers: (_Transition transition) -> void
361
+
362
+ def add_watch: (Symbol name, Hash[Symbol, untyped] options) -> Array[ConditionWatch]
363
+
364
+ def add_trigger: (Symbol name, Hash[Symbol, untyped] options) -> Array[Trigger]
365
+
366
+ def run_watches: () -> void
367
+
368
+ def determine_initial_state: () -> void
369
+
370
+ def handle_user_command: (String cmd) -> void
371
+
372
+ def process_running?: (?bool force) -> bool
373
+
374
+ def clean_threads: () -> void
375
+
376
+ def daemonize?: () -> bool
377
+
378
+ def monitor_children?: () -> bool
379
+
380
+ def signal_process: (Integer | String | Symbol code) -> bool
381
+
382
+ def cache_actual_pid?: () -> bool
383
+
384
+ def actual_pid: () -> Integer?
385
+
386
+ def pid_from_file: () -> Integer?
387
+
388
+ def pid_from_command: () -> Integer?
389
+
390
+ def positive_pid: (Integer pid) -> Integer?
391
+
392
+ def actual_pid=: (Integer pid) -> void
393
+
394
+ def clear_pid: () -> nil
395
+
396
+ def unlink_pid: () -> void
397
+
398
+ def skip_ticks_for: (seconds seconds) -> Integer
399
+
400
+ def skipping_ticks?: () -> bool?
401
+
402
+ def refresh_children!: () -> void
403
+ end
404
+
405
+ # Keeps a short history of a process's events
406
+ class ProcessStatistics
407
+ type event = [Symbol | String, String?, Time]
408
+
409
+ STRFTIME: String
410
+ EVENTS_TO_PERSIST: Integer
411
+
412
+ attr_reader events: Util::RotationalArray[event]
413
+
414
+ def initialize: () -> void
415
+
416
+ def record_event: (Symbol | String event, String? reason) -> Util::RotationalArray[event]
417
+
418
+ def to_s: () -> String
419
+ end
420
+
421
+ # Processes that share a group name. Processes without one are in the
422
+ # group named nil.
423
+ class Group
424
+ attr_accessor name: String?
425
+ attr_accessor processes: Array[Process]
426
+ attr_accessor logger: Logger
427
+ # Not used by bluepill
428
+ attr_accessor process_logger: untyped
429
+
430
+ def initialize: (String? name, ?{ ?logger: Logger } options) -> void
431
+
432
+ def add_process: (Process process) -> Array[Process]
433
+
434
+ def tick: () -> void
435
+
436
+ def determine_initial_state: () -> void
437
+
438
+ # Proxied events, which return the affected processes as group:process
439
+
440
+ def start: (?String? process_name) -> Array[String]
441
+
442
+ def unmonitor: (?String? process_name) -> Array[String]
443
+
444
+ def stop: (?String? process_name) -> Array[String]
445
+
446
+ def restart: (?String? process_name) -> Array[String]
447
+
448
+ def status: (?String? process_name) -> Array[String]
449
+ end
450
+
451
+ # Logs to a file or syslog, with nested prefixes
452
+ class Logger
453
+ # Where a Logger without a parent logger writes: a LoggerAdapter, or
454
+ # Syslog
455
+ interface _Backend
456
+ def emerg: (String message) -> void
457
+
458
+ def alert: (String message) -> void
459
+
460
+ def crit: (String message) -> void
461
+
462
+ def err: (String message) -> void
463
+
464
+ def warning: (String message) -> void
465
+
466
+ def notice: (String message) -> void
467
+
468
+ def info: (String message) -> void
469
+
470
+ def debug: (String message) -> void
471
+ end
472
+
473
+ type options = {
474
+ ?logger: Logger,
475
+ ?prefix: String?,
476
+ ?stdout: bool,
477
+ ?log_file: String?,
478
+ ?identity: String
479
+ }
480
+
481
+ LOG_METHODS: Array[Symbol]
482
+
483
+ # A ::Logger that also answers to the syslog method names
484
+ class LoggerAdapter < ::Logger
485
+ LOGGER_EQUIVALENTS: Hash[Symbol, Symbol]
486
+
487
+ alias err error
488
+
489
+ alias warning warn
490
+
491
+ alias emerg fatal
492
+
493
+ alias alert warn
494
+
495
+ alias crit fatal
496
+
497
+ alias notice info
498
+ end
499
+
500
+ @options: options
501
+ @logger: Logger | (Object & _Backend)
502
+ @prefix: String?
503
+ @stdout: bool?
504
+ @prefixes: Hash[String?, Logger]
505
+
506
+ def initialize: (?options options) -> void
507
+
508
+ def emerg: (_ToS msg, ?Array[String?] prefix) -> void
509
+
510
+ def alert: (_ToS msg, ?Array[String?] prefix) -> void
511
+
512
+ def crit: (_ToS msg, ?Array[String?] prefix) -> void
513
+
514
+ def err: (_ToS msg, ?Array[String?] prefix) -> void
515
+
516
+ def warning: (_ToS msg, ?Array[String?] prefix) -> void
517
+
518
+ def notice: (_ToS msg, ?Array[String?] prefix) -> void
519
+
520
+ def info: (_ToS msg, ?Array[String?] prefix) -> void
521
+
522
+ def debug: (_ToS msg, ?Array[String?] prefix) -> void
523
+
524
+ def prefix_with: (String? prefix) -> Logger
525
+
526
+ def reopen: () -> void
527
+
528
+ private
529
+
530
+ def create_logger: () -> (Object & _Backend)
531
+
532
+ def require_syslog: () -> bool
533
+ end
534
+
535
+ # A value a ConditionWatch got, and whether it passed the check
536
+ class HistoryValue < Struct[_ToS]
537
+ attr_accessor value: _ToS
538
+ attr_accessor critical: bool
539
+
540
+ def self.new: (_ToS value, bool critical) -> HistoryValue
541
+ end
542
+
543
+ # Runs a process condition every so often, and fires its events when the
544
+ # check fails often enough
545
+ class ConditionWatch
546
+ EMPTY_ARRAY: Array[Symbol]
547
+
548
+ attr_accessor logger: Logger
549
+ attr_accessor name: Symbol
550
+
551
+ @fires: Array[Symbol]
552
+ @every: Integer
553
+ @times: Array[Integer]
554
+ @include_children: bool
555
+ @history: Util::RotationalArray[HistoryValue]
556
+ @process_condition: ProcessConditions::ProcessCondition[untyped]
557
+ @last_ran_at: Integer?
558
+
559
+ def initialize: (Symbol name, ?Hash[Symbol, untyped] options) -> void
560
+
561
+ # Returns the events to fire
562
+ def run: (Integer? pid, ?Integer tick_number) -> Array[Symbol]
563
+
564
+ def clear_history!: () -> Util::RotationalArray[HistoryValue]
565
+
566
+ def fired?: () -> bool
567
+
568
+ def to_s: () -> String
569
+ end
570
+
571
+ # Reacts to a process's state transitions. Subclasses register themselves
572
+ # by their underscored name.
573
+ class Trigger
574
+ self.@implementations: Hash[Symbol, singleton(Trigger)]
575
+
576
+ def self.inherited: (singleton(Trigger) klass) -> void
577
+
578
+ def self.[]: %a{implicitly-returns-nil} (Symbol name) -> singleton(Trigger)
579
+
580
+ attr_accessor process: Process
581
+ attr_accessor logger: Logger
582
+ attr_accessor mutex: Thread::Mutex
583
+ attr_accessor scheduled_events: Array[[Symbol, Thread]]
584
+
585
+ def initialize: (Process process, ?Hash[Symbol, untyped] options) -> void
586
+
587
+ def reset!: () -> void
588
+
589
+ def notify: (_Transition transition) -> void
590
+
591
+ def dispatch!: (Symbol event) -> bool
592
+
593
+ def schedule_event: (Symbol event, seconds delay) -> Thread
594
+
595
+ def cancel_all_events: () -> void
596
+ end
597
+
598
+ module Triggers
599
+ # Unmonitors a process that starts or restarts too often, and starts it
600
+ # again later
601
+ class Flapping < Trigger
602
+ TRIGGER_STATES: Array[Symbol]
603
+ PARAMS: Array[Symbol]
604
+
605
+ attr_accessor times: Integer
606
+ attr_accessor within: Integer
607
+ attr_accessor retry_in: Integer
608
+ attr_reader timeline: Util::RotationalArray[Integer]
609
+
610
+ def initialize: (Process process, ?Hash[Symbol, untyped] options) -> void
611
+
612
+ def notify: (_Transition transition) -> void
613
+
614
+ def reset!: () -> void
615
+
616
+ def check_flapping: () -> void
617
+ end
618
+ end
619
+
620
+ # self in an application block
621
+ class AppProxy
622
+ APP_ATTRIBUTES: Array[Symbol]
623
+
624
+ attr_accessor working_dir: String?
625
+ attr_accessor uid: String?
626
+ attr_accessor gid: String?
627
+ attr_accessor environment: Hash[_ToS, _ToS]?
628
+ attr_accessor auto_start: bool?
629
+ attr_reader app: Application
630
+
631
+ def initialize: (_ToS app_name, Hash[Symbol, untyped] options) -> void
632
+
633
+ # The block runs with self set to the ProcessProxy, and also gets it as
634
+ # an argument
635
+ def process: (String process_name) { (ProcessProxy process) [self: ProcessProxy] -> void } -> Array[Process]
636
+ end
637
+
638
+ # Builds processes from a process block in the config
639
+ class ProcessFactory
640
+ @@process_keys: Hash[Array[String?], nil]
641
+ @@pid_files: Hash[String, nil]
642
+
643
+ @process_block: process_block
644
+
645
+ attr_reader attributes: Hash[Symbol, untyped]
646
+
647
+ def initialize: (Hash[Symbol, untyped] attributes, process_block process_block) -> void
648
+
649
+ def create_process: (String name, String pids_dir) -> Process
650
+
651
+ def create_child_process: (String name, Integer pid, Logger logger) -> Process
652
+
653
+ private
654
+
655
+ def assign_default_pid_file: (String process_name, String pids_dir) -> String?
656
+
657
+ def validate_process!: (ProcessProxy process) -> void
658
+
659
+ def validate_unique_process!: (ProcessProxy process) -> void
660
+
661
+ def validate_required_attributes!: (ProcessProxy process) -> void
662
+
663
+ def validate_unique_pid_file!: () -> void
664
+
665
+ def validate_stop_signals!: () -> void
666
+
667
+ def warn_unless_pid_known: (ProcessProxy process, String? default_pid_file) -> void
668
+
669
+ def config_error!: (String message) -> bot
670
+
671
+ def validate_child_process!: (ProcessProxy child) -> void
672
+ end
673
+
674
+ # self in a process block. Any attribute can be set with `name = value`,
675
+ # `name value` or `name!` (for true), and read back with `name`.
676
+ class ProcessProxy
677
+ attr_reader attributes: Hash[Symbol, untyped]
678
+ attr_reader watches: Hash[Symbol, Hash[Symbol, untyped]]
679
+ attr_reader name: String
680
+
681
+ def initialize: (String process_name, Hash[Symbol, untyped] attributes, process_block process_block) -> void
682
+
683
+ # Sets or reads an attribute
684
+ def method_missing: (Symbol name, *untyped args) -> untyped
685
+
686
+ def respond_to_missing?: (Symbol name, bool? include_private) -> bool
687
+
688
+ def checks: (Symbol name, ?Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
689
+
690
+ # The block configures the children like a process block
691
+ def monitor_children: () ?{ (ProcessProxy child) [self: ProcessProxy] -> void } -> process_block?
692
+
693
+ def to_process: () -> Process
694
+ end
695
+
696
+ # Memoizes ps output for a tick, and runs commands as daemons or until
697
+ # they finish
698
+ module System
699
+ # Options for daemonize and execute_blocking
700
+ type command_options = {
701
+ ?uid: String?,
702
+ ?gid: String?,
703
+ ?working_dir: String?,
704
+ ?environment: Hash[_ToS, _ToS]?,
705
+ ?pid_file: String?,
706
+ ?logger: Logger,
707
+ ?stdin: String?,
708
+ ?stdout: String?,
709
+ ?stderr: String?,
710
+ ?supplementary_groups: Array[String]?
711
+ }
712
+
713
+ type blocking_result = { exit_code: Integer?, stdout: String, stderr: String }
714
+
715
+ type grandchild_pipes = { out: [IO, IO], err: [IO, IO], result: IO }
716
+
717
+ APPEND_MODE: String
718
+
719
+ IDX_MAP: { pid: Integer, ppid: Integer, pcpu: Integer, rss: Integer, etime: Integer, command: Integer }
720
+
721
+ PS_MUTEX: Thread::Mutex
722
+
723
+ # The store lives on the module. Steep checks the module functions as
724
+ # instance methods, so it needs the instance variable too.
725
+ self.@store: Hash[Symbol, ps_table | String]
726
+ @store: Hash[Symbol, ps_table | String]
727
+
728
+ # Raised when ps could not be run, so there is no data for this tick
729
+ class PsError < StandardError
730
+ end
731
+
732
+ def self.pid_alive?: (Integer pid) -> bool
733
+
734
+ def self.cpu_usage: (Integer? pid, boolish include_children) -> Float?
735
+
736
+ def self.memory_usage: (Integer? pid, boolish include_children) -> Float?
737
+
738
+ def self.running_time: (Integer? pid) -> Integer?
739
+
740
+ def self.command: (Integer? pid) -> String?
741
+
742
+ def self.get_children: (Integer? parent_pid) -> Array[Integer]
743
+
744
+ def self.delete_if_exists: (String? filename) -> void
745
+
746
+ def self.store: () -> Hash[Symbol, ps_table | String]
747
+
748
+ def self.reset_data: () -> void
749
+
750
+ def self.ps_axu: () -> ps_table
751
+
752
+ def self.parse_elapsed_time: (String? str) -> Integer
753
+
754
+ # Returns the daemon's pid
755
+ def self.daemonize: (String cmd, ?command_options options) -> Integer?
756
+
757
+ def self.execute_blocking: (String cmd, ?command_options options) -> blocking_result
758
+
759
+ def self.run_blocking_child: (String cmd, command_options options, IO result_pipe) -> bot
760
+
761
+ def self.exec_blocking_grandchild: (String cmd, command_options options, grandchild_pipes pipes) -> bot
762
+
763
+ def self.setup_command_environment: (command_options options) -> void
764
+
765
+ def self.drop_privileges: (String? uid, String? gid, Array[String]? supplementary_groups) -> void
766
+
767
+ def self.can_write_pid_file: (String pid_file, Logger logger) -> bool
768
+
769
+ def self.redirect_io: (String? io_in, String? io_out, String? io_err) -> void
770
+ end
771
+
772
+ # Remembers the pids and process groups of the processes bluepill starts,
773
+ # so it can kill any left over from an earlier run
774
+ module ProcessJournal
775
+ self.@logger: Logger
776
+ self.@journal_base_dir: String
777
+
778
+ def self.logger: () -> Logger
779
+
780
+ def self.journal_base_dir: () -> String
781
+
782
+ # Only the first logger is kept
783
+ def self.logger=: (Logger new_logger) -> Logger
784
+
785
+ def self.base_dir=: (String base_dir) -> void
786
+
787
+ def self.skip_pid?: (Integer? pid) -> bool
788
+
789
+ def self.skip_pgid?: (Integer? pgid) -> bool
790
+
791
+ def self.acquire_atomic_fs_lock: [T] (String name) { () -> T } -> T
792
+
793
+ def self.clear_all_atomic_fs_locks: () -> void
794
+
795
+ def self.pid_journal_filename: (String journal_name) -> String
796
+
797
+ def self.pgid_journal_filename: (String journal_name) -> String
798
+
799
+ def self.pid_journal: (String filename) -> Array[Integer]
800
+
801
+ def self.pgid_journal: (String filename) -> Array[Integer]
802
+
803
+ def self.clear_atomic_fs_lock: (String name) -> void
804
+
805
+ def self.kill_all_from_all_journals: () -> void
806
+
807
+ def self.kill_all_from_journal: (String journal_name) -> void
808
+
809
+ def self.kill_all_pgids_from_journal: (String journal_name) -> void
810
+
811
+ def self.kill_all_pids_from_journal: (String journal_name) -> void
812
+
813
+ def self.kill_all_from_journal_file: (String filename, Array[Integer] ids, String label, Integer sign) -> void
814
+
815
+ def self.signal_journaled: (String signal, Array[Integer] ids, String label, Integer sign) -> void
816
+
817
+ def self.append_pgid_to_journal: (String journal_name, Integer? pgid) -> void
818
+
819
+ def self.append_pid_to_journal: (String journal_name, Integer? pid) -> void
820
+ end
821
+
822
+ module ProcessConditions
823
+ def self.[]: (Symbol | String name) -> singleton(ProcessCondition)
824
+
825
+ # Checks something about a process. V is the type of value that run
826
+ # returns and check tests.
827
+ class ProcessCondition[V]
828
+ @options: Hash[Symbol, untyped]
829
+
830
+ def initialize: (?Hash[Symbol, untyped] options) -> void
831
+
832
+ def run: (Integer? pid, boolish include_children) -> V
833
+
834
+ # Whether the value is fine
835
+ def check: (V value) -> bool
836
+
837
+ def format_value: (V value) -> V
838
+ end
839
+
840
+ class AlwaysTrue < ProcessCondition[Integer]
841
+ def run: (Integer? pid, boolish include_children) -> Integer
842
+
843
+ def check: (Integer value) -> true
844
+ end
845
+
846
+ class CpuUsage < ProcessCondition[Float]
847
+ @below: Numeric
848
+
849
+ def run: (Integer? pid, boolish include_children) -> Float
850
+
851
+ def check: (Float value) -> bool
852
+ end
853
+
854
+ # Seconds since the file changed, or the error that stopped bluepill
855
+ # from finding out
856
+ class FileTime < ProcessCondition[Float | StandardError | nil]
857
+ @below: Numeric
858
+ @filename: String
859
+
860
+ def run: (Integer? pid, boolish include_children) -> (Float | StandardError | nil)
861
+
862
+ def check: (Float | StandardError | nil value) -> bool
863
+ end
864
+
865
+ class Http < ProcessCondition[Net::HTTPResponse | StandardError]
866
+ @uri: URI::HTTP
867
+ @kind: Class
868
+ @pattern: Regexp?
869
+ @open_timeout: Integer
870
+ @read_timeout: Integer
871
+
872
+ def run: (Integer? pid, boolish include_children) -> (Net::HTTPResponse | StandardError)
873
+
874
+ def check: (Net::HTTPResponse | StandardError value) -> bool
875
+
876
+ private
877
+
878
+ def hide_net_http_bug: [T] () { () -> T } -> T
879
+ end
880
+
881
+ # Memory usage in kilobytes
882
+ class MemUsage < ProcessCondition[Float]
883
+ MB: Integer
884
+ FORMAT_STR: String
885
+ MB_LABEL: String
886
+ KB_LABEL: String
887
+
888
+ @below: Numeric
889
+
890
+ def run: (Integer? pid, boolish include_children) -> Float
891
+
892
+ def check: (Float value) -> bool
893
+
894
+ def format_value: (Float value) -> String
895
+ end
896
+
897
+ # Seconds since the process started
898
+ class RunningTime < ProcessCondition[Integer]
899
+ @below: Numeric
900
+
901
+ def run: (Integer? pid, boolish include_children) -> Integer
902
+
903
+ def check: (Integer value) -> bool
904
+ end
905
+
906
+ # The process's command line, which says <defunct> for a zombie
907
+ class ZombieProcess < ProcessCondition[String?]
908
+ def run: (Integer? pid, boolish include_children) -> String?
909
+
910
+ def check: (String? value) -> bool
911
+ end
912
+ end
913
+
914
+ module Util
915
+ # An array that drops its oldest element when it grows past its capacity
916
+ class RotationalArray[Elem] < Array[Elem]
917
+ @capacity: Integer
918
+
919
+ def initialize: (Integer size) -> void
920
+
921
+ def push: (Elem value) -> self
922
+
923
+ alias << push
924
+ end
925
+ end
926
+ end