Class: Kaal::CLI

Inherits:
Thor
  • Object
show all
Defined in:
lib/kaal/cli.rb

Overview

Thor-powered CLI for plain-Ruby usage.

Defined Under Namespace

Modules: Helpers

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.exit_on_failure?Boolean

Returns:

  • (Boolean)


180
181
182
# File 'lib/kaal/cli.rb', line 180

def self.exit_on_failure?
  true
end

.install_foreground_signal_handlers(signal_state) ⇒ Object



190
191
192
193
194
195
# File 'lib/kaal/cli.rb', line 190

def self.install_foreground_signal_handlers(signal_state)
  installer = SignalHandlerInstaller.new
  installer.install do |signal, previous_handler|
    shutdown_scheduler(signal: signal, signal_state: signal_state, previous_handler: previous_handler)
  end
end

.restore_signal_handlers(previous_handlers) ⇒ Object



197
198
199
200
201
202
203
# File 'lib/kaal/cli.rb', line 197

def self.restore_signal_handlers(previous_handlers)
  previous_handlers.each do |signal, handler|
    Signal.trap(signal, handler)
  rescue StandardError
    nil
  end
end

.shutdown_scheduler(signal:, signal_state:, previous_handler: nil, shell: nil) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/kaal/cli.rb', line 205

def self.shutdown_scheduler(signal:, signal_state:, previous_handler: nil, shell: nil)
  shell_instance = shell || Thor::Base.shell.new
  return if signal_state[:shutdown_complete]

  if signal_state[:graceful_shutdown_started]
    signal_state[:force_exit_requested] = true
    shell_instance.warn("Received #{signal} again; forcing scheduler shutdown")
    Thread.main.raise(Interrupt)
    return
  end

  signal_state[:graceful_shutdown_started] = true
  shell_instance.say("Received #{signal}, stopping Kaal scheduler...")
  stopped = Kaal.stop!(timeout: 30)
  if stopped
    signal_state[:shutdown_complete] = true
    shell_instance.say('Kaal scheduler stopped')
  else
    shell_instance.warn('Kaal scheduler stop timed out; send TERM/INT again to force exit')
  end
ensure
  SignalHandlerChain.new(signal: signal, previous_handler: previous_handler, logger: Kaal.logger).call(signal)
end

.write_file(path, contents) ⇒ Object



184
185
186
187
188
# File 'lib/kaal/cli.rb', line 184

def self.write_file(path, contents)
  return if File.exist?(path)

  File.write(path, contents)
end

Instance Method Details

#explain(expression) ⇒ Object



163
164
165
# File 'lib/kaal/cli.rb', line 163

def explain(expression)
  say(Kaal.to_human(expression))
end

#initObject



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/kaal/cli.rb', line 102

def init
  root = File.expand_path(options[:root])
  backend = options[:backend]
  writer = self.class
  FileUtils.mkdir_p(File.join(root, 'config'))

  writer.write_file(File.join(root, 'config', 'kaal.rb'), render_config_template(backend))
  writer.write_file(File.join(root, 'config', 'scheduler.yml'), scheduler_template)

  say("Initialized Kaal project for #{backend} backend")
end

#next(expression) ⇒ Object

Raises:

  • (Thor::Error)


169
170
171
172
173
174
175
176
177
178
# File 'lib/kaal/cli.rb', line 169

def next(expression)
  cron = Fugit.parse_cron(expression)
  raise Thor::Error, "Invalid cron expression: #{expression}" unless cron

  current = Time.now.utc
  options[:count].to_i.times do
    current = cron.next_time(current).to_t.utc
    say(current.iso8601)
  end
end

#startObject



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/kaal/cli.rb', line 115

def start
  load_project!

  signal_state = {
    graceful_shutdown_started: false,
    shutdown_complete: false,
    force_exit_requested: false
  }
  previous_handlers = Kaal::CLI.install_foreground_signal_handlers(signal_state)

  begin
    thread = Kaal.start!
    raise Thor::Error, 'scheduler is already running' unless thread

    say('Kaal scheduler started in foreground')
    thread.join
  rescue Interrupt
    raise Thor::Error, 'shutdown timed out; forced exit requested' if signal_state[:force_exit_requested]

    Kaal::CLI.shutdown_scheduler(signal: 'INT', signal_state: signal_state, shell: shell)
  ensure
    Kaal::CLI.restore_signal_handlers(previous_handlers)
  end
end

#statusObject



141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/kaal/cli.rb', line 141

def status
  load_project!
  registered = Kaal.registered
  say("Kaal v#{Kaal::VERSION}")
  say("Running: #{Kaal.running?}")
  say("Tick interval: #{Kaal.tick_interval}s")
  say("Window lookback: #{Kaal.window_lookback}s")
  say("Window lookahead: #{Kaal.window_lookahead}s")
  say("Lease TTL: #{Kaal.lease_ttl}s")
  say("Namespace: #{Kaal.namespace}")
  say("Registered jobs: #{registered.length}")
  registered.each { |entry| say("  - #{entry.key} (#{entry.cron})") }
end

#tickObject



156
157
158
159
160
# File 'lib/kaal/cli.rb', line 156

def tick
  load_project!
  Kaal.tick!
  say('Kaal tick completed')
end