Class: Ringleader::CLI

Inherits:
Object
  • Object
show all
Includes:
Celluloid::Logger
Defined in:
lib/ringleader/cli.rb

Constant Summary collapse

RC_FILE =
File.expand_path('~/.ringleaderrc')

Instance Method Summary collapse

Instance Method Details

#configure_loggingObject



36
37
38
39
40
41
42
43
44
45
# File 'lib/ringleader/cli.rb', line 36

def configure_logging
  # set to INFO at first to hide celluloid's shutdown message until after
  # opts are validated.
  Celluloid.logger.level = ::Logger::INFO
  format = "%5s %s.%06d | %s\n"
  date_format = "%H:%M:%S"
  Celluloid.logger.formatter = lambda do |severity, time, progname, msg|
    format % [severity, time.strftime(date_format), time.usec, msg]
  end
end

#die(msg) ⇒ Object



47
48
49
50
# File 'lib/ringleader/cli.rb', line 47

def die(msg)
  error msg
  exit(-1)
end

#merge_rc_opts(opts) ⇒ Object



137
138
139
140
141
142
143
144
# File 'lib/ringleader/cli.rb', line 137

def merge_rc_opts(opts)
  [:verbose, :host, :port, :boring].each do |option_name|
    if rc_opts.has_key?(option_name) && !opts["#{option_name}_given".to_sym]
      opts[option_name] = rc_opts[option_name]
    end
  end
  opts
end

#parserObject



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/ringleader/cli.rb', line 52

def parser
  @parser ||= Trollop::Parser.new do

    version Ringleader::VERSION

    banner <<-banner
ringleader - your socket app server host

SYNOPSIS

Ringleader runs, monitors, and proxies socket applications. Upon receiving a new
connection to a given port, ringleader will start the correct application and
proxy the connection to the now-running app. It also supports automatic timeout
for shutting down applications that haven't been used recently.

USAGE

ringleader <config.yml> [options+]

APPLICATIONS

Ringleader supports any application that runs in the foreground (not
daemonized), and listens on a port. It expects applications to be well-behaved,
that is, respond appropriately to SIGINT for graceful shutdown.

When first starting an app, ringleader will wait for the application's port to
open, at which point it will proxy the incoming connection through.

SIGNALS

While ringleader is running, Ctrl+C (SIGINT) will gracefully shut down
ringleader as well as the applications it's hosting.

CONFIGURATION

Ringleader requires a configuration .yml file to operate. The file should look
something like this:

---
# name of app (used in logging)
main_app:

  # Required settings
  dir: "~/apps/main"       # Working directory
  command: "foreman start" # The command to run to start up the app server.
                           # Executed under "bash -c".
  server_port: 3000        # The port ringleader listens on
  app_port: 4000           # The port the application listens on

  # Optional settings
  host: 127.0.0.1          # The host ringleader should listen on
  idle_timeout: 6000       # Idle timeout in seconds, 0 for infinite
  startup_timeout: 180     # Application startup timeout
  disabled: true           # Set the app to be disabled when ringleader starts
  env:                     # Override or set environment variables inherited
    FOO: hello             # from the current environment. Use nil to unset a
    BAR: nil               # var.
  kill_with: INT           # Signal to use to kill the process tree with. Use
                           # TERM or KILL if the default is leaving zombies.
  run_on_load: false       # Set this to true to start an app when ringleader
                           # loads.

  # If you have an application managed by rvm, this setting automatically
  # adds the rvm-specific shell setup before executing the given command.
  # This supersedes the `command` setting.
  rvm: "foreman start"

  # Likewise for rbenv:
  rbenv: "foreman start"

OPTIONS
    banner

    opt :verbose, "log at debug level",
      :short => "-v", :default => false
    opt :host, "host for web control panel",
      :short => "-H", :default => "localhost"
    opt :port, "port for the web control panel",
      :short => "-p", :default => 42000
    opt :boring, "use boring colors instead of a fabulous rainbow",
      :short => "-b", :default => false

  end
end

#rc_optsObject



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/ringleader/cli.rb', line 146

def rc_opts
  unless @rc_opts
    if File.readable?(RC_FILE)
      info "reading options from ~/.ringleaderrc"
      @rc_opts = parser.parse File.read(RC_FILE).strip.split(/\s+/)
    else
      @rc_opts = {}
    end
  end
  @rc_opts
end

#run(argv) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/ringleader/cli.rb', line 7

def run(argv)
  configure_logging

  opts = nil
  Trollop.with_standard_exception_handling parser do
    opts = merge_rc_opts(parser.parse(argv))
  end

  die "must provide a filename" if argv.empty?
  die "could not find config file #{argv.first}" unless File.exist?(argv.first)

  if opts.verbose
    Celluloid.logger.level = ::Logger::DEBUG
  end

  apps = Config.new(argv.first, opts.boring).apps

  controller = Controller.new(apps)
  Server.new(controller, opts.host, opts.port)

  # gracefully die instead of showing an interrupted sleep below
  trap("INT") do
    controller.stop
    exit
  end

  sleep
end